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# Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org> 24# 25 26IPFILTER_FMRI="svc:/network/ipfilter:default" 27ETC_IPF_DIR=/etc/ipf 28IPNATCONF=`/usr/bin/svcprop -p config/ipnat_config_file $IPFILTER_FMRI \ 29 2>/dev/null` 30if [ $? -eq 1 ]; then 31 IPNATCONF=$ETC_IPF_DIR/ipnat.conf 32fi 33IPPOOLCONF=`/usr/bin/svcprop -p config/ippool_config_file $IPFILTER_FMRI \ 34 2>/dev/null` 35if [ $? -eq 1 ]; then 36 IPPOOLCONF=$ETC_IPF_DIR/ippool.conf 37fi 38VAR_IPF_DIR=/var/run/ipf 39IPFILCONF=$VAR_IPF_DIR/ipf.conf 40IP6FILCONF=$VAR_IPF_DIR/ipf6.conf 41IPFILOVRCONF=$VAR_IPF_DIR/ipf_ovr.conf 42IP6FILOVRCONF=$VAR_IPF_DIR/ipf6_ovr.conf 43IPF_LOCK=/var/run/ipflock 44CONF_FILES="" 45CONF6_FILES="" 46NAT_FILES="" 47IPF_SUFFIX=".ipf" 48IPF6_SUFFIX=".ipf6" 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" 68APPLY2_6_PROP="apply_to_6" 69EXCEPTIONS_PROP="exceptions" 70EXCEPTIONS_6_PROP="exceptions_6" 71TARGET_PROP="target" 72TARGET_6_PROP="target_6" 73BLOCKPOL_PROP="block_policy" 74 75FW_CONFIG_DEF_PG="firewall_config_default" 76FW_CONFIG_OVR_PG="firewall_config_override" 77CUSTOM_FILE_PROP="custom_policy_file" 78CUSTOM_FILE_6_PROP="custom_policy_file_6" 79OPEN_PORTS_PROP="open_ports" 80 81PREFIX_HOST="host:" 82PREFIX_NET="network:" 83PREFIX_POOL="pool:" 84PREFIX_IF="if:" 85 86GLOBAL_CONFIG="" 87GLOBAL_POLICY="" 88GLOBAL_BLOCK_POLICY="" 89 90SERVINFO=/usr/lib/servinfo 91 92# 93# Get value(s) for given property from either firewall_config_default or 94# firewall_config_override property groups. 95# 96# global_get_prop_value pg_name propname 97# pg_name - FW_CONFIG_DEF_PG or FW_CONFIG_OVR_PG 98# propname - property name 99# 100global_get_prop_value() 101{ 102 target_pg=$1 103 prop=$2 104 105 [ "$1" != $FW_CONFIG_OVR_PG -a "$1" != $FW_CONFIG_DEF_PG ] && return 106 107 [ "$1" == $FW_CONFIG_DEF_PG ] && extra_pg=$FW_CONFIG_OVR_PG || \ 108 extra_pg=$FW_CONFIG_DEF_PG 109 110 value=`echo $GLOBAL_CONFIG | awk '{ 111 found=0 112 for (i=1; i<=NF; i++) { 113 if (found == 1) { 114 if (index($i, target_pg) == 1 || index($i, extra_pg) == 1) 115 break; 116 117 print $i; 118 } 119 120 if (split($i, values, "/") < 2) 121 continue; 122 123 if (values[1] == target_pg && values[2] == prop) 124 found=1; 125 } 126 }' target_pg=$target_pg prop=$prop extra_pg=$extra_pg` 127 128 # Return 129 echo "$value" 130} 131 132# 133# Initialize and cache network/ipfilter configuration, global configuration. 134# 135# Since an SMF service configuration may get updated during the execution of the 136# service method, it's best to read all relevant configuration via one svcprop 137# invocation and cache it for later use. 138# 139# This function reads and stores relevant configuration into GLOBAL_CONFIG and 140# initializes the GLOBAL_POLICY and GLOBAL_BLOCK_POLICY variables. GLOBAL_CONFIG 141# is a string containing pg/prop and their corresponding values (i.e. svcprop -p 142# pg fmri output). To get values for a certain pg/prop, use 143# global_get_prop_value(). 144# 145global_init() 146{ 147 GLOBAL_CONFIG=`svcprop -p ${FW_CONFIG_OVR_PG} -p ${FW_CONFIG_DEF_PG} \ 148 $IPF_FMRI 2>/dev/null | awk '{$2=" "; print $0}'` 149 150 GLOBAL_POLICY=`global_get_prop_value $FW_CONFIG_DEF_PG $POLICY_PROP` 151 GLOBAL_BLOCK_POLICY=`global_get_prop_value $FW_CONFIG_DEF_PG \ 152 $BLOCKPOL_PROP` 153} 154 155# 156# Given a service, gets its config pg name 157# 158get_config_pg() 159{ 160 if [ "$1" = "$IPF_FMRI" ]; then 161 echo "$FW_CONFIG_DEF_PG" 162 else 163 echo "$FW_CONFIG_PG" 164 fi 165 return 0 166} 167 168# 169# Given a service, gets its firewall policy 170# 171get_policy() 172{ 173 config_pg=`get_config_pg $1` 174 svcprop -p $config_pg/${POLICY_PROP} $1 2>/dev/null 175} 176 177# 178# block policy can be set to "return", which will expand into 179# separate block rules for tcp (block return-rst ...) and all other 180# protocols (block return-icmp-as-dest ...) 181# 182get_block_policy() 183{ 184 config_pg=`get_config_pg $1` 185 svcprop -p $config_pg/${BLOCKPOL_PROP} $1 2>/dev/null 186} 187 188# 189# Given a service, gets its source address exceptions for IPv4 190# 191get_exceptions() 192{ 193 config_pg=`get_config_pg $1` 194 exceptions=`svcprop -p $config_pg/${EXCEPTIONS_PROP} $1 2>/dev/null` 195 echo $exceptions | sed -e 's/\\//g' 196} 197 198# 199# Given a service, gets its source address exceptions for IPv6 200# 201get_exceptions_6() 202{ 203 config_pg=`get_config_pg $1` 204 exceptions6=`svcprop -p $config_pg/${EXCEPTIONS_6_PROP} $1 2>/dev/null` 205 echo $exceptions6 | sed -e 's/\\//g' 206} 207 208# 209# Given a service, gets its firewalled source addresses for IPv4 210# 211get_apply2_list() 212{ 213 config_pg=`get_config_pg $1` 214 apply2=`svcprop -p $config_pg/${APPLY2_PROP} $1 2>/dev/null` 215 echo $apply2 | sed -e 's/\\//g' 216} 217 218# 219# Given a service, gets its firewalled source addresses for IPv6 220# 221get_apply2_6_list() 222{ 223 config_pg=`get_config_pg $1` 224 apply2_6=`svcprop -p $config_pg/${APPLY2_6_PROP} $1 2>/dev/null` 225 echo $apply2_6 | sed -e 's/\\//g' 226} 227 228# 229# Given a service, gets its firewalled target addresses for IPv4 230# 231get_target_list() 232{ 233 config_pg=`get_config_pg $1` 234 target=`svcprop -p $config_pg/${TARGET_PROP} $1 2>/dev/null` 235 [ -z "$target" -o "$target" = '""' ] && target=any 236 echo $target | sed -e 's/\\//g' 237} 238 239# 240# Given a service, gets its firewalled target addresses for IPv6 241# 242get_target_6_list() 243{ 244 config_pg=`get_config_pg $1` 245 target6=`svcprop -p $config_pg/${TARGET_6_PROP} $1 2>/dev/null` 246 [ -z "$target6" -o "$target6" = '""' ] && target6=any 247 echo $target6 | sed -e 's/\\//g' 248} 249 250check_ipf_dir() 251{ 252 [ -d $VAR_IPF_DIR ] && return 0 253 mkdir $VAR_IPF_DIR >/dev/null 2>&1 || return 1 254} 255 256# 257# fmri_to_file fmri suffix 258# 259fmri_to_file() 260{ 261 check_ipf_dir || return 1 262 fprefix="${VAR_IPF_DIR}/`echo $1 | tr -s '/:' '__'`" 263 echo "${fprefix}${2}" 264} 265 266# 267# Return service's enabled property 268# 269service_is_enabled() 270{ 271 # 272 # Temporary enabled state overrides the persistent state 273 # so check it first. 274 # 275 enabled_ovr=`svcprop -c -p general_ovr/enabled $1 2>/dev/null` 276 if [ -n "$enabled_ovr" ]; then 277 [ "$enabled_ovr" = "true" ] && return 0 || return 1 278 fi 279 280 enabled=`svcprop -c -p general/enabled $1 2>/dev/null` 281 [ -n "$enabled" -a "$enabled" = "true" ] && return 0 || return 1 282} 283 284# 285# Return whether service is desired state 286# 287# Args: fmri state 288# Return: 289# 0 - desired state is service's current state 290# 1 - desired state is not service's current state 291# 292service_check_state() 293{ 294 # 295 # Make sure we're done with ongoing state transition 296 # 297 while [ "`svcprop -p restarter/next_state $1`" != "$SMF_NONE" ]; do 298 sleep 1 299 done 300 301 [ "`svcprop -p restarter/state $1`" = "$2" ] && return 0 || return 1 302} 303 304# 305# Deny/Allow list stores values in the form "host:addr", "network:addr/netmask", 306# "pool:number", and "if:interface". This function returns the 307# IP(addr or addr/netmask) value or a pool number. 308# 309get_IP() 310{ 311 value_is_interface $1 && return 1 312 echo "$1" | sed -n -e "s,^${PREFIX_POOL}\(.*\),pool/\1,p" \ 313 -e "s,^${PREFIX_HOST}\(.*\),\1,p" \ 314 -e "s,^${PREFIX_NET}\(.*\),\1,p" \ 315 -e "s,^any,any,p" 316} 317 318get_interface() 319{ 320 value_is_interface $1 || return 1 321 scratch=`echo "$1" | sed -e "s/^${PREFIX_IF}//"` 322 323 ifconfig $scratch >/dev/null 2>&1 || return 1 324 echo $scratch | sed -e 's/:.*//' 325} 326 327# 328# 329# 330value_is_interface() 331{ 332 [ -z "$1" ] && return 1 333 echo $1 | grep "^${PREFIX_IF}" >/dev/null 2>&1 334} 335 336# 337# Remove rules in given file from active list without restarting ipfilter 338# 339remove_rules() 340{ 341 [ -f "$1" ] && ipf $2 -r -f $1 >/dev/null 2>&1 342} 343 344remove_nat_rules() 345{ 346 [ -f "$1" ] && ipnat -r -f $1 >/dev/null 2>&1 347} 348 349check_ipf_syntax() 350{ 351 ipf $2 -n -f $1 >/dev/null 2>&1 352} 353 354check_nat_syntax() 355{ 356 ipnat -n -f $1 >/dev/null 2>&1 357} 358 359unique_ports() 360{ 361 echo $* | xargs -n 1 echo | sort -u 362} 363 364file_get_ports() 365{ 366 ipf $2 -n -v -f $1 2>/dev/null | sed -n -e \ 367 's/.*to.* port = \([a-z0-9]*\).*/\1/p' | uniq | \ 368 awk '{if (length($0) > 1) {printf("%s ", $1)}}' 369} 370 371get_active_ports() 372{ 373 ipfstat $1 -io 2>/dev/null | sed -n -e \ 374 's/.*to.* port = \([a-z0-9]*\).*/\1/p' | uniq | \ 375 awk '{if (length($0) > 1) {printf("%s ",$1)}}' 376} 377 378# 379# Given two list of ports, return failure if there's a duplicate. 380# 381sets_check_duplicate() 382{ 383 # 384 # If either list is empty, there isn't any conflict. 385 # 386 [ -z "$1" -o -z "$2" ] && return 0 387 388 for p in $1; do 389 for ap in $2; do 390 [ "$p" = "$ap" ] && return 1 391 done 392 done 393 394 return 0 395} 396 397# 398# Given a file containing ipf rules, check the syntax and verify 399# the rules don't conflict, use same port number, with active 400# rules (ipfstat -io output). 401# 402update_check_ipf_rules() 403{ 404 check_ipf_syntax $1 $2 || return 1 405 406 lports=`file_get_ports $1 $2` 407 lactive_ports=`get_active_ports $2` 408 409 sets_check_duplicate "$lports" "$lactive_ports" || return 1 410} 411 412server_port_list="" 413server_port_list_6="" 414 415# 416# Given a file containing ipf rules, check the syntax and verify 417# the rules don't conflict with already processed services. 418# 419# The list of processed services' ports are maintained in the global 420# variables 'server_port_list' and 'server_port_list_6'. 421# 422check_ipf_rules() 423{ 424 425 check_ipf_syntax $1 $2 || return 1 426 427 lports=`file_get_ports $1 $2` 428 429 if [ "$2" = "-6" ]; then 430 sets_check_duplicate "$lports" "$server_port_list_6" || return 1 431 server_port_list_6="$server_port_list_6 $lports" 432 else 433 sets_check_duplicate "$lports" "$server_port_list" || return 1 434 server_port_list="$server_port_list $lports" 435 fi 436 437 return 0 438} 439 440prepend_new_rules() 441{ 442 check_ipf_syntax $1 $2 && tail -r $1 | sed -e 's/^[a-z]/@0 &/' | \ 443 ipf $2 -f - >/dev/null 2>&1 444} 445 446append_new_rules() 447{ 448 check_ipf_syntax $1 $2 && ipf $2 -f $1 >/dev/null 2>&1 449} 450 451append_new_nat_rules() 452{ 453 check_nat_syntax $1 && ipnat -f $1 >/dev/null 2>&1 454} 455 456# 457# get port information from string of the form "proto:{port | port-port}" 458# 459tuple_get_port() 460{ 461 port_str=`echo "$1" | sed -e 's/ //g; s/\\\//g; s/.*://' 2>/dev/null` 462 [ -z "$port_str" ] && return 1 463 464 echo $port_str | grep "-" >/dev/null 465 if [ $? -eq 0 ]; then 466 echo $port_str | grep '^[0-9]\{1,5\}-[0-9]\{1,5\}$' >/dev/null || \ 467 return 1 468 ports=`echo $port_str | ( IFS=- read a b ; \ 469 [ $a \-le $b ] && echo $a $b || echo $b $a )` 470 471 for p in $ports; do 472 [ $p -gt 65535 ] && return 1 473 done 474 echo "$ports" 475 else 476 # 477 # port_str is a single port, verify and return it. 478 # 479 echo "$port_str" | grep '^[0-9]\{1,5\}$' >/dev/null || return 1 480 [ $port_str -gt 65535 ] && return 1 481 echo "$port_str" 482 fi 483} 484 485# 486# get proto info from string of the form "{tcp | udp}:port" 487# 488tuple_get_proto() 489{ 490 proto=`echo "$1" | sed -e 's/ //g; s/:.*//' 2>/dev/null` 491 [ -z "$proto" ] && return 0 492 493 [ "$proto" = "tcp" -o "$proto" = "udp" ] && echo $proto || return 1 494 return 0 495} 496 497ipf_get_lock() 498{ 499 newpid=$$ 500 501 if [ -f "$IPF_LOCK/pid" ]; then 502 curpid=`cat $IPF_LOCK/pid 2>/dev/null` 503 [ "$curpid" = "$newpid" ] && return 0 504 505 # 506 # Clear lock if the owning process is no longer around. 507 # 508 ps -p $curpid >/dev/null 2>&1 || rm -r $IPF_LOCK >/dev/null 2>&1 509 fi 510 511 # 512 # Grab the lock 513 # 514 while :; do 515 mkdir $IPF_LOCK 2>/dev/null && break; 516 sleep 1 517 done 518 echo $newpid > $IPF_LOCK/pid 519} 520 521# 522# Remove lock if it's ours 523# 524ipf_remove_lock() 525{ 526 if [ -f "$IPF_LOCK/pid" ]; then 527 [ "`cat $IPF_LOCK/pid`" = "$$" ] && rm -r $IPF_LOCK 528 fi 529 return 0 530} 531 532# 533# Make IPFILCONF, /var/tmp/ipf/ipf.conf, a symlink to the input file argument. 534# 535custom_set_symlink() 536{ 537 # 538 # Nothing to do if the input file doesn't exist. 539 # 540 [ ! -f "$1" ] && return 0 541 542 check_ipf_dir || return 1 543 544 rm $IPFILCONF >/dev/null 2>&1 545 ln -s $1 $IPFILCONF >/dev/null 2>&1 546} 547 548# 549# Make IP6FILCONF, /var/tmp/ipf/ipf6.conf, a symlink to the input file argument. 550# 551custom_set_symlink_6() 552{ 553 # 554 # Nothing to do if the input file doesn't exist. 555 # 556 [ ! -f "$1" ] && return 0 557 558 check_ipf_dir || return 1 559 560 rm $IP6FILCONF >/dev/null 2>&1 561 ln -s $1 $IP6FILCONF >/dev/null 2>&1 562} 563 564# 565# New file replaces original file if they have different content 566# 567replace_file() 568{ 569 orig=$1 570 new=$2 571 572 # 573 # IPFILCONF may be a symlink, remove it if that's the case 574 # 575 if [ -L "$orig" ]; then 576 rm $orig 577 touch $orig 578 fi 579 580 check_ipf_dir || return 1 581 mv $new $orig && return 0 || return 1 582} 583 584# 585# Given a service, gets the following details for ipf rule: 586# - policy 587# - protocol 588# - port(IANA port obtained by running servinfo) 589# 590process_server_svc() 591{ 592 service=$1 593 policy=`get_policy ${service}` 594 595 # 596 # Empties service's rules file so callers won't use existing rule if 597 # we fail here. 598 # 599 file=`fmri_to_file $service $IPF_SUFFIX` 600 file6=`fmri_to_file $service $IPF6_SUFFIX` 601 [ -z "$file" ] && return 1 602 echo "# $service" >${file} 603 echo "# $service" >${file6} 604 605 # 606 # Nothing to do if policy is "use_global" 607 # 608 [ "$policy" = "use_global" ] && return 0 609 610 restarter=`svcprop -p general/restarter $service 2>/dev/null` 611 if [ "$restarter" = "$INETDFMRI" ]; then 612 iana_name=`svcprop -p inetd/name $service 2>/dev/null` 613 isrpc=`svcprop -p inetd/isrpc $service 2>/dev/null` 614 else 615 iana_name=`svcprop -p $FW_CONTEXT_PG/name $service 2>/dev/null` 616 isrpc=`svcprop -p $FW_CONTEXT_PG/isrpc $service 2>/dev/null` 617 fi 618 619 # 620 # Bail if iana_name isn't defined. Services with static rules 621 # like nis/client don't need to generate rules using 622 # iana name and protocol information. 623 # 624 [ -z "$iana_name" ] && return 1 625 626 # 627 # RPC services 628 # 629 if [ "$isrpc" = "true" ]; then 630 # The ports used for IPv6 are usually also reachable 631 # through IPv4, so generate IPv4 rules for them, too. 632 tports=`$SERVINFO -R -p -t -s $iana_name 2>/dev/null` 633 tports6=`$SERVINFO -R -p -t6 -s $iana_name 2>/dev/null` 634 if [ -n "$tports" -o -n "$tports6" ]; then 635 tports=`unique_ports $tports $tports6` 636 for tport in $tports; do 637 generate_rules $service $policy "tcp" \ 638 $tport $file 639 done 640 fi 641 642 if [ -n "$tports6" ]; then 643 for tport6 in $tports6; do 644 generate_rules $service $policy "tcp" \ 645 $tport6 $file6 _6 646 done 647 fi 648 649 uports=`$SERVINFO -R -p -u -s $iana_name 2>/dev/null` 650 uports6=`$SERVINFO -R -p -u6 -s $iana_name 2>/dev/null` 651 if [ -n "$uports" ]; then 652 uports=`unique_ports $uports $uports6` 653 for uport in $uports; do 654 generate_rules $service $policy "udp" \ 655 $uport $file 656 done 657 fi 658 659 if [ -n "$uports6" ]; then 660 for uport6 in $uports6; do 661 generate_rules $service $policy "udp" \ 662 $uport6 $file6 _6 663 done 664 fi 665 666 return 0 667 fi 668 669 # 670 # Get the IANA port and supported protocols(tcp and udp) 671 # 672 tport=`$SERVINFO -p -t -s $iana_name 2>&1` 673 if [ $? -eq 0 -a -n "$tport" ]; then 674 generate_rules $service $policy "tcp" $tport $file 675 fi 676 677 tport6=`$SERVINFO -p -t6 -s $iana_name 2>&1` 678 if [ $? -eq 0 -a -n "$tport6" ]; then 679 generate_rules $service $policy "tcp" $tport6 $file6 _6 680 fi 681 682 uport=`$SERVINFO -p -u -s $iana_name 2>&1` 683 if [ $? -eq 0 -a -n "$uport" ]; then 684 generate_rules $service $policy "udp" $uport $file 685 fi 686 687 uport6=`$SERVINFO -p -u6 -s $iana_name 2>&1` 688 if [ $? -eq 0 -a -n "$uport6" ]; then 689 generate_rules $service $policy "udp" $uport6 $file6 _6 690 fi 691 692 return 0 693} 694 695# 696# Given a service's name, policy, protocol and port, generate ipf rules 697# - list of host/network/interface to apply policy 698# 699# A 'use_global' policy inherits the system-wided Global Default policy 700# from network/ipfilter. For {deny | allow} policies, the rules are 701# ordered as: 702# 703# - make exceptions to policy for those in "exceptions" list 704# - apply policy to those specified in "apply_to" list 705# - policy rule 706# 707generate_rules() 708{ 709 service=$1 710 mypolicy=$2 711 proto=$3 712 port=$4 713 out=$5 714 _6=$6 715 716 # 717 # Default mode is to inherit from global's policy 718 # 719 [ "$mypolicy" = "use_global" ] && return 0 720 721 tcp_opts="" 722 [ "$proto" = "tcp" ] && tcp_opts="flags S keep state keep frags" 723 724 block_policy=`get_block_policy $1` 725 if [ "$block_policy" = "use_global" ]; then 726 block_policy=${GLOBAL_BLOCK_POLICY} 727 fi 728 729 if [ "$block_policy" = "return" ]; then 730 [ "$proto" = "tcp" ] && block_policy="return-rst" 731 [ "$proto" != "tcp" ] && block_policy="return-icmp-as-dest" 732 else 733 block_policy="" 734 fi 735 736 iplist=`get_target${_6}_list $service` 737 738 # 739 # Allow all if policy is 'none' 740 # 741 if [ "$mypolicy" = "none" ]; then 742 for ip in $iplist; do 743 daddr=`get_IP ${ip}` 744 [ -z "$daddr" -o "$daddr" = '""' ] && continue 745 echo "pass in log quick proto ${proto} from any to ${daddr}" \ 746 "port = ${port} ${tcp_opts}" >>${out} 747 done 748 return 0 749 fi 750 751 # 752 # For now, let's concern ourselves only with incoming traffic. 753 # 754 [ "$mypolicy" = "deny" ] && { ecmd="pass"; acmd="block ${block_policy}"; } 755 [ "$mypolicy" = "allow" ] && { ecmd="block ${block_policy}"; acmd="pass"; } 756 757 for name in `get_exceptions${_6} $service`; do 758 [ -z "$name" -o "$name" = '""' ] && continue 759 760 ifc=`get_interface $name` 761 if [ $? -eq 0 -a -n "$ifc" ]; then 762 for ip in $iplist; do 763 daddr=`get_IP ${ip}` 764 [ -z "$daddr" -o "$daddr" = '""' ] && continue 765 echo "${ecmd} in log quick on ${ifc} from any to" \ 766 "${daddr} port = ${port}" >>${out} 767 done 768 continue 769 fi 770 771 saddr=`get_IP ${name}` 772 if [ $? -eq 0 -a -n "$saddr" ]; then 773 for ip in $iplist; do 774 daddr=`get_IP ${ip}` 775 [ -z "$daddr" -o "$daddr" = '""' ] && continue 776 echo "${ecmd} in log quick proto ${proto} from ${saddr}" \ 777 "to ${daddr} port = ${port} ${tcp_opts}" >>${out} 778 done 779 fi 780 done 781 782 for name in `get_apply2${_6}_list $service`; do 783 [ -z "$name" -o "$name" = '""' ] && continue 784 785 ifc=`get_interface $name` 786 if [ $? -eq 0 -a -n "$ifc" ]; then 787 for ip in $iplist; do 788 daddr=`get_IP ${ip}` 789 [ -z "$daddr" -o "$daddr" = '""' ] && continue 790 echo "${acmd} in log quick on ${ifc} from any to" \ 791 "${daddr} port = ${port}" >>${out} 792 done 793 continue 794 fi 795 796 saddr=`get_IP ${name}` 797 if [ $? -eq 0 -a -n "$saddr" ]; then 798 for ip in $iplist; do 799 daddr=`get_IP ${ip}` 800 [ -z "$daddr" -o "$daddr" = '""' ] && continue 801 echo "${acmd} in log quick proto ${proto} from ${saddr}" \ 802 "to ${daddr} port = ${port} ${tcp_opts}" >>${out} 803 done 804 fi 805 done 806 807 for ip in $iplist; do 808 daddr=`get_IP ${ip}` 809 [ -z "$daddr" -o "$daddr" = '""' ] && continue 810 echo "${ecmd} in log quick proto ${proto} from any to ${daddr}" \ 811 "port = ${port} ${tcp_opts}" >>${out} 812 done 813 814 return 0 815} 816 817# 818# Service has either IANA ports and proto or its own firewall method to 819# generate the rules. 820# 821# - if service has a custom method, use it to populate its rules 822# - if service has a firewall_config pg, use process_server_svc 823# 824# Argument - fmri 825# 826process_service() 827{ 828 # 829 # Don't process network/ipfilter 830 # 831 [ "$1" = "$IPF_FMRI" ] && return 0 832 833 service_check_state $1 $SMF_MAINT && return 1 834 835 method=`svcprop -p $FW_CONTEXT_PG/$METHOD_PROP $1 2>/dev/null | \ 836 sed 's/\\\//g'` 837 if [ -n "$method" -a "$method" != '""' ]; then 838 ( exec $method $1 >/dev/null ) 839 else 840 svcprop -p $FW_CONFIG_PG $1 >/dev/null 2>&1 || return 1 841 process_server_svc $1 || return 1 842 fi 843 return 0 844} 845 846# 847# Generate rules for protocol/port defined in firewall_config_default/open_ports 848# property. These are non-service programs whose network resource info are 849# defined as "{tcp | upd}:{PORT | PORT-PORT}". Essentially, these programs need 850# some specific local ports to be opened. For example, BitTorrent clients need to 851# have 6881-6889 opened. 852# 853process_nonsvc_progs() 854{ 855 out=$1 856 echo "# Non-service programs rules" >>${out} 857 progs=`global_get_prop_value $FW_CONFIG_DEF_PG $OPEN_PORTS_PROP` 858 859 for prog in $progs; do 860 [ -z "$prog" -o "$prog" = '""' ] && continue 861 862 port=`tuple_get_port $prog` 863 [ $? -eq 1 -o -z "$port" ] && continue 864 865 proto=`tuple_get_proto $prog` 866 [ $? -eq 1 ] && continue 867 868 set -- $port 869 if [ $# -gt 1 ]; then 870 if [ -z "$proto" ]; then 871 echo "pass in log quick from any to any" \ 872 "port ${1} >< ${2}" >>${out} 873 else 874 echo "pass in log quick proto ${proto} from any" \ 875 "to any port ${1} >< ${2}" >>${out} 876 fi 877 else 878 if [ -z "$proto" ]; then 879 echo "pass in log quick from any to any" \ 880 "port = ${1}" >>${out} 881 else 882 echo "pass in log quick proto ${proto} from any" \ 883 "to any port = ${1}" >>${out} 884 fi 885 fi 886 done 887 888 return 0 889} 890 891# 892# Generate a new /etc/ipf/ipf.conf. If firewall policy is 'none', 893# ipf.conf is empty . 894# 895create_global_rules() 896{ 897 if [ "$GLOBAL_POLICY" = "custom" ]; then 898 file=`global_get_prop_value $FW_CONFIG_DEF_PG $CUSTOM_FILE_PROP` 899 file6=`global_get_prop_value $FW_CONFIG_DEF_PG $CUSTOM_FILE_6_PROP` 900 901 [ -n "$file" ] && custom_set_symlink $file 902 [ -n "$file6" ] && custom_set_symlink_6 $file6 903 904 return 0 905 fi 906 907 TEMP=`mktemp /var/run/ipf.conf.pid$$.XXXXXX` 908 TEMP6=`mktemp /var/run/ipf6.conf.pid$$.XXXXXX` 909 process_nonsvc_progs $TEMP 910 process_nonsvc_progs $TEMP6 911 912 echo "# Global Default rules" >>${TEMP} 913 echo "# Global Default rules" >>${TEMP6} 914 if [ "$GLOBAL_POLICY" != "none" ]; then 915 echo "pass out log quick all keep state" >>${TEMP} 916 echo "pass out log quick all keep state" >>${TEMP6} 917 fi 918 919 case "$GLOBAL_POLICY" in 920 'none') 921 # No rules 922 replace_file ${IPFILCONF} ${TEMP} 923 replace_file ${IP6FILCONF} ${TEMP6} 924 return $? 925 ;; 926 927 'deny') 928 ecmd="pass" 929 acmd="block" 930 ;; 931 932 'allow') 933 ecmd="block" 934 acmd="pass" 935 ;; 936 *) 937 return 1; 938 ;; 939 esac 940 941 for name in `global_get_prop_value $FW_CONFIG_DEF_PG $EXCEPTIONS_PROP`; do 942 [ -z "$name" -o "$name" = '""' ] && continue 943 944 ifc=`get_interface $name` 945 if [ $? -eq 0 -a -n "$ifc" ]; then 946 echo "${ecmd} in log quick on ${ifc} all" >>${TEMP} 947 continue 948 fi 949 950 addr=`get_IP ${name}` 951 if [ $? -eq 0 -a -n "$addr" ]; then 952 echo "${ecmd} in log quick from ${addr} to any" >>${TEMP} 953 fi 954 955 done 956 957 for name in `global_get_prop_value $FW_CONFIG_DEF_PG $EXCEPTIONS_6_PROP`; do 958 [ -z "$name" -o "$name" = '""' ] && continue 959 960 ifc=`get_interface $name` 961 if [ $? -eq 0 -a -n "$ifc" ]; then 962 echo "${ecmd} in log quick on ${ifc} all" >>${TEMP6} 963 continue 964 fi 965 966 addr=`get_IP ${name}` 967 if [ $? -eq 0 -a -n "$addr" ]; then 968 echo "${ecmd} in log quick from ${addr} to any" >>${TEMP6} 969 fi 970 971 done 972 973 for name in `global_get_prop_value $FW_CONFIG_DEF_PG $APPLY2_PROP`; do 974 [ -z "$name" -o "$name" = '""' ] && continue 975 976 ifc=`get_interface $name` 977 if [ $? -eq 0 -a -n "$ifc" ]; then 978 echo "${acmd} in log quick on ${ifc} all" >>${TEMP} 979 continue 980 fi 981 982 addr=`get_IP ${name}` 983 if [ $? -eq 0 -a -n "$addr" ]; then 984 echo "${acmd} in log quick from ${addr} to any" >>${TEMP} 985 fi 986 done 987 988 for name in `global_get_prop_value $FW_CONFIG_DEF_PG $APPLY2_6_PROP`; do 989 [ -z "$name" -o "$name" = '""' ] && continue 990 991 ifc=`get_interface $name` 992 if [ $? -eq 0 -a -n "$ifc" ]; then 993 echo "${acmd} in log quick on ${ifc} all" >>${TEMP6} 994 continue 995 fi 996 997 addr=`get_IP ${name}` 998 if [ $? -eq 0 -a -n "$addr" ]; then 999 echo "${acmd} in log quick from ${addr} to any" >>${TEMP6} 1000 fi 1001 done 1002 1003 if [ "$GLOBAL_POLICY" = "allow" ]; then 1004 # 1005 # Allow DHCP(v6) traffic if running as a DHCP client 1006 # 1007 /sbin/netstrategy | grep dhcp >/dev/null 2>&1 1008 if [ $? -eq 0 ]; then 1009 echo "pass out log quick from any port = 68" \ 1010 "keep state" >>${TEMP} 1011 echo "pass in log quick from any to any port = 68" >>${TEMP} 1012 1013 echo "pass out log quick from any port = 546" \ 1014 "keep state" >>${TEMP6} 1015 echo "pass in log quick from any to any port = 546" >>${TEMP6} 1016 fi 1017 echo "block in log all" >>${TEMP} 1018 echo "block in log all" >>${TEMP6} 1019 fi 1020 1021 replace_file ${IPFILCONF} ${TEMP} 1022 replace_file ${IP6FILCONF} ${TEMP6} 1023 return $? 1024} 1025 1026# 1027# Generate a new /etc/ipf/ipf_ovr.conf, the override system-wide policy. It's 1028# a simplified policy that doesn't support 'exceptions' entities. 1029# 1030# If firewall policy is "none", no rules are generated. 1031# 1032# Note that "pass" rules don't have "quick" as we don't want 1033# them to override services' block rules. 1034# 1035create_global_ovr_rules() 1036{ 1037 # 1038 # Simply empty override file if global policy is 'custom' 1039 # 1040 if [ "$GLOBAL_POLICY" = "custom" ]; then 1041 echo "# 'custom' global policy" >$IPFILOVRCONF 1042 echo "# 'custom' global policy" >$IP6FILOVRCONF 1043 return 0 1044 fi 1045 1046 # 1047 # Get and process override policy 1048 # 1049 ovr_policy=`global_get_prop_value $FW_CONFIG_OVR_PG $POLICY_PROP` 1050 if [ "$ovr_policy" = "none" ]; then 1051 echo "# global override policy is 'none'" >$IPFILOVRCONF 1052 echo "# global override policy is 'none'" >$IP6FILOVRCONF 1053 return 0 1054 fi 1055 1056 TEMP=`mktemp /var/run/ipf_ovr.conf.pid$$.XXXXXX` 1057 [ "$ovr_policy" = "deny" ] && acmd="block in log quick" 1058 [ "$ovr_policy" = "allow" ] && acmd="pass in log" 1059 1060 apply2_list=`global_get_prop_value $FW_CONFIG_OVR_PG $APPLY2_PROP` 1061 for name in $apply2_list; do 1062 [ -z "$name" -o "$name" = '""' ] && continue 1063 1064 ifc=`get_interface $name` 1065 if [ $? -eq 0 -a -n "$ifc" ]; then 1066 echo "${acmd} on ${ifc} all" >>${TEMP} 1067 continue 1068 fi 1069 1070 addr=`get_IP ${name}` 1071 if [ $? -eq 0 -a -n "$addr" ]; then 1072 echo "${acmd} from ${addr} to any" >>${TEMP} 1073 fi 1074 done 1075 1076 apply2_6_list=`global_get_prop_value $FW_CONFIG_OVR_PG $APPLY2_6_PROP` 1077 for name in $apply2_6_list; do 1078 [ -z "$name" -o "$name" = '""' ] && continue 1079 1080 ifc=`get_interface $name` 1081 if [ $? -eq 0 -a -n "$ifc" ]; then 1082 echo "${acmd} on ${ifc} all" >>${TEMP6} 1083 continue 1084 fi 1085 1086 addr=`get_IP ${name}` 1087 if [ $? -eq 0 -a -n "$addr" ]; then 1088 echo "${acmd} from ${addr} to any" >>${TEMP6} 1089 fi 1090 done 1091 1092 replace_file ${IPFILOVRCONF} ${TEMP} 1093 replace_file ${IP6FILOVRCONF} ${TEMP6} 1094 return $? 1095} 1096 1097# 1098# Service is put into maintenance state due to its invalid firewall 1099# definition and/or policy. 1100# 1101svc_mark_maintenance() 1102{ 1103 svcadm mark maintenance $1 >/dev/null 2>&1 1104 1105 date=`date` 1106 echo "[ $date ${0}: $1 has invalid ipf configuration. ]" 1107 echo "[ $date ${0}: placing $1 in maintenance. ]" 1108 1109 # 1110 # Move service's rule files to another location since 1111 # they're most likely invalid. 1112 # 1113 ipfile=`fmri_to_file $1 $IPF_SUFFIX` 1114 [ -f "$ipfile" ] && mv $ipfile "$ipfile.bak" 1115 ip6file=`fmri_to_file $1 $IPF6_SUFFIX` 1116 [ -f "$ip6file" ] && mv $ip6file "$ip6file.bak" 1117 1118 natfile=`fmri_to_file $1 $NAT_SUFFIX` 1119 [ -f "$natfile" ] && mv $natfile "$natfile.bak" 1120 1121 return 0 1122} 1123 1124svc_is_server() 1125{ 1126 svcprop -p $FW_CONFIG_PG $1 >/dev/null 2>&1 1127} 1128 1129# 1130# Create rules for enabled firewalling and client services. 1131# - obtain the list of enabled services and process them 1132# - save the list of rules file for later use 1133# 1134create_services_rules() 1135{ 1136 # 1137 # Do nothing if global policy is 'custom' 1138 # 1139 [ "$GLOBAL_POLICY" = "custom" ] && return 0 1140 1141 ipf_get_lock 1142 1143 # 1144 # Get all enabled services 1145 # 1146 allsvcs=`svcprop -cf -p general/enabled -p general_ovr/enabled '*' \ 1147 2>/dev/null | sed -n 's,^\(svc:.*\)/:properties/.* true$,\1,p' | sort -u` 1148 1149 # 1150 # Process enabled services 1151 # 1152 for s in $allsvcs; do 1153 service_is_enabled $s || continue 1154 process_service $s || continue 1155 1156 ipfile=`fmri_to_file $s $IPF_SUFFIX` 1157 if [ -n "$ipfile" -a -r "$ipfile" ]; then 1158 check_ipf_syntax $ipfile 1159 if [ $? -ne 0 ]; then 1160 svc_mark_maintenance $s 1161 continue 1162 fi 1163 1164 svc_is_server $s 1165 if [ $? -eq 0 ]; then 1166 check_ipf_rules $ipfile 1167 if [ $? -ne 0 ]; then 1168 svc_mark_maintenance $s 1169 continue 1170 fi 1171 fi 1172 CONF_FILES="$CONF_FILES $ipfile" 1173 fi 1174 1175 ip6file=`fmri_to_file $s $IPF6_SUFFIX` 1176 if [ -n "$ip6file" -a -r "$ip6file" ]; then 1177 check_ipf_syntax $ip6file -6 1178 if [ $? -ne 0 ]; then 1179 svc_mark_maintenance $s 1180 continue 1181 fi 1182 1183 svc_is_server $s 1184 if [ $? -eq 0 ]; then 1185 check_ipf_rules $ip6file -6 1186 if [ $? -ne 0 ]; then 1187 svc_mark_maintenance $s 1188 continue 1189 fi 1190 fi 1191 CONF6_FILES="$CONF6_FILES $ip6file" 1192 fi 1193 1194 natfile=`fmri_to_file $s $NAT_SUFFIX` 1195 if [ -n "$natfile" -a -r "$natfile" ]; then 1196 check_nat_syntax $natfile 1197 if [ $? -ne 0 ]; then 1198 svc_mark_maintenance $s 1199 continue 1200 fi 1201 1202 NAT_FILES="$NAT_FILES $natfile" 1203 fi 1204 done 1205 1206 ipf_remove_lock 1207 return 0 1208} 1209 1210# 1211# We update a services ipf ruleset in the following manners: 1212# - service is disabled, tear down its rules. 1213# - service is disable or refreshed(online), setup or update its rules. 1214# 1215service_update_rules() 1216{ 1217 svc=$1 1218 1219 ipfile=`fmri_to_file $svc $IPF_SUFFIX` 1220 ip6file=`fmri_to_file $svc $IPF6_SUFFIX` 1221 [ -n "$ipfile" ] && remove_rules $ipfile 1222 [ -n "$ip6file" ] && remove_rules $ip6file -6 1223 1224 [ -z "$ipfile" -a -z "$ip6file" ] && return 0 1225 1226 natfile=`fmri_to_file $svc $NAT_SUFFIX` 1227 [ -n "$natfile" ] && remove_nat_rules $natfile 1228 1229 # 1230 # Don't go further if service is disabled or in maintenance. 1231 # 1232 service_is_enabled $svc || return 0 1233 service_check_state $1 $SMF_MAINT && return 0 1234 1235 process_service $svc || return 1 1236 if [ -f "$ipfile" ]; then 1237 check_ipf_syntax $ipfile 1238 if [ $? -ne 0 ]; then 1239 svc_mark_maintenance $svc 1240 return 1 1241 fi 1242 fi 1243 1244 if [ -f "$ip6file" ]; then 1245 check_ipf_syntax $ip6file -6 1246 if [ $? -ne 0 ]; then 1247 svc_mark_maintenance $svc 1248 return 1 1249 fi 1250 fi 1251 1252 if [ -f "$natfile" ]; then 1253 check_nat_syntax $natfile 1254 if [ $? -ne 0 ]; then 1255 svc_mark_maintenance $svc 1256 return 1 1257 fi 1258 fi 1259 1260 if [ -f "$ipfile" ]; then 1261 svc_is_server $svc 1262 if [ $? -eq 0 ]; then 1263 update_check_ipf_rules $ipfile 1264 if [ $? -ne 0 ]; then 1265 svc_mark_maintenance $svc 1266 return 1 1267 fi 1268 fi 1269 1270 prepend_new_rules $ipfile 1271 1272 # 1273 # reload Global Override rules to 1274 # maintain correct ordering. 1275 # 1276 remove_rules $IPFILOVRCONF 1277 prepend_new_rules $IPFILOVRCONF 1278 fi 1279 1280 if [ -f "$ip6file" ]; then 1281 svc_is_server $svc 1282 if [ $? -eq 0 ]; then 1283 update_check_ipf_rules $ip6file -6 1284 if [ $? -ne 0 ]; then 1285 svc_mark_maintenance $svc 1286 return 1 1287 fi 1288 fi 1289 1290 prepend_new_rules $ip6file -6 1291 1292 # 1293 # reload Global Override rules to 1294 # maintain correct ordering. 1295 # 1296 remove_rules $IP6FILOVRCONF -6 1297 prepend_new_rules $IP6FILOVRCONF -6 1298 fi 1299 1300 [ -f "$natfile" ] && append_new_nat_rules $natfile 1301 1302 return 0 1303} 1304 1305# 1306# Call the service_update_rules with appropriate svc fmri. 1307# 1308# This is called from '/lib/svc/method/ipfilter fw_update' whenever 1309# a service is disabled/enabled/refreshed. 1310# 1311service_update() 1312{ 1313 svc=$1 1314 ret=0 1315 1316 # 1317 # If ipfilter isn't online or global policy is 'custom', 1318 # nothing should be done. 1319 # 1320 [ "$GLOBAL_POLICY" = "custom" ] && return 0 1321 service_check_state $SMF_FMRI $SMF_ONLINE || return 0 1322 1323 ipf_get_lock 1324 service_update_rules $svc || ret=1 1325 1326 ipf_remove_lock 1327 return $ret 1328} 1329 1330# 1331# Initialize global configuration 1332# 1333global_init 1334 1335