# !/bin/bash2
#
# chkconfig: 345 11 91
# description: Simple firewall and masquerading setup for RedHat Linux \
#              with 2.2 kernels and ipchains
# --------------------------------------------------------------------------
# Author: Bodo Bauer <bb@ricochet.net>
#         Ruediger Oertel <ro@suse.de> adapted for SuSE boot layout
# 				Mirko Zeibig <mirko.zeibig@gmx.de> adapted for RH6.0
# /etc/rc.d/init.d/firewall
#
# Changes:
#
# bb 01/14/99 - v2.2 Rewrite of the whole thing
#             * changed to 'ipchains' to make it working with 2.1 kernel
#             * enhanced start and status messages
#             * dynamic IP addresses can be inserted in configutration with 
#               special string IP@<device-name>
#             * only self created rules and chains will be removed on 'stop'
#               (instead of global flush in earlier version...)
# mz 07/27/99 - made it work with RedHat
# --------------------------------------------------------------------------

FW_PATH=/etc/sysconfig/firewall

# Read settings 
. $FW_PATH/config

# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

# Check that networking is up.
[ ${NETWORKING} = "no" ] && exit 0

# Version
VERSION="firewall v2.2"

# Determine the base and follow a runlevel link name.
base=${0##*/}
link=${base#*[SK][0-9][0-9]}

# Force execution if not called by a runlevel directory.
test $link = $base && FW_START=yes
test "$FW_START" = yes || exit 0

# used tools
IPCHAINS=/sbin/ipchains
AWK=/usr/bin/awk
GREP=/bin/grep
IFCONFIG=/sbin/ifconfig
SED=/bin/sed

#for debugging
#IPCHAINS="echo ipchains "

# list of trusted hosts
FRIENDS=$FW_PATH/fw-friends

# Internal hosts with access to the ouside
INOUT=$FW_PATH/fw-inout

# list of ssh hosts
SSH=$FW_PATH/fw-ssh

if test "$FW_LOG_DENY" = "yes" ; then
	DENY_FLAG="$DENY_FLAG -l"
fi

if test "$FW_LOG_ACCEPT" = "yes" ; then
	ACC_FLAG="$ACC_FLAG -l"
fi

# Dynamic IP hack. The string IP@<dev>, where <dev> is a defined
# network interface, will be replaced by it's IP address of this device.
dynip() {
	for var in `$AWK 'BEGIN{FS="="} /^FW_|^MSQ_/ {print $1}' $FW_PATH/config` ; do
		list=`eval echo $\`echo $var\``
		if test  "$list" != ""; then
			tmp=""
			flag=0
			for ip in $list ; do
				if test "${ip:0:3}" = "IP@" ; then
					# found special string
					devip=`$IFCONFIG ${ip:3} | $AWK '/inet addr:/ { print $2 }' | $SED s/addr://`
					tmp="$tmp$devip "
					flag=1
				else
					tmp="$tmp$ip "
				fi
			done
			if test $flag = 1 ; then
			echo "  $var set to $tmp"
			eval "$var=\"$tmp\""
			fi
		fi
	done
}

if test -f /proc/net/ip_fwchains -a `cat /proc/sys/net/ipv4/ip_forward` -eq 1 ; then 
	case $1 in
		start)
			# check for dynamic addresses
			dynip
			if test "$FW_START" = "yes" ; then
				if `cat /proc/net/ip_fwnames | $GREP user_fw > /dev/null` ; then
					echo -n "Custom chains already exist"
					failure "start"
				else
					# Seems like everything is OK, so lets set up the firewall
					echo "Starting $VERSION "

					# first create a new chain incoming traffic
					$IPCHAINS -N user_fw
					# deny all traffic until all rules are set up on the eincoming queue
					$IPCHAINS -A user_fw -s 0/0 -d 0/0 $DENY_FLAG -j DENY 

					# direct incoming packages to user_fw
					for net in $FW_LOCALNETS ; do
						for dev in $FW_WORLD_DEV ; do
							$IPCHAINS -A input -s 0/0 -d $net -i $dev -j user_fw
						done
					done

					# redirect all outgoing traffic to user_out, if needed 
					if test "$FW_INOUT" = "yes" -o "$FW_TRANSPROXY_OUT" != "" ; then
						$IPCHAINS -N user_out
						for net in $FW_LOCALNETS ; do
							for dev in $FW_INT_DEV ; do
								$IPCHAINS -A input -d 0/0 -s $net -i $dev -j user_out
							done
						done
					fi

					# set up firewall rules in chain user_fw

					# prohibit spoofing on kernel level but if there is an
					# router outside the network, let it have access
					if test "$FW_ROUTER" != "" ; then
						echo -n "  Bypass spoofing rules for: "
						for host in $FW_ROUTER ; do
							$IPCHAINS -A user_fw -s $host -d 0/0 $ACC -j ACCEPT
							echo -n "$host "
						done
						echo ""
						for dev in $FW_WORLD_DEV ; do
							if test -f /proc/sys/net/ipv4/conf/$dev/rp_filter ; then
								echo "0" > /proc/sys/net/ipv4/conf/$dev/rp_filter
							fi
						done
					else
						for dev in $FW_WORLD_DEV ; do
							if test -f /proc/sys/net/ipv4/conf/$dev/rp_filter ; then
								echo "2" > /proc/sys/net/ipv4/conf/$dev/rp_filter
							fi
						done
					fi
					for net in $FW_LOCALNETS ; do
						$IPCHAINS -A user_fw -s $net -d 0/0 $DENY_FLAG -j DENY 
					done

					# give unlimited access to trusted hosts
					if test "$FW_FRIENDS" = "yes" -a -f $FRIENDS ; then
						hosts=`${AWK} '/^#/ { next; } { print $1 }' ${FRIENDS}`
						if test "$hosts" != "" ; then
							echo -n "  Trusted hosts: "
							for host in $hosts ; do
								$IPCHAINS -A user_fw -s $host $ACC_FLAG -j ACCEPT
								echo -n "$host "
							done
							echo ""
						fi
					fi

					# permit ssh for listed hosts
					if test "$FW_SSH" = "yes" -a -f $SSH ; then
						hosts=`${AWK} '/^#/ { next; } { print $1 }' ${SSH}`
						if test "$hosts" != "" ; then
							echo -n "  SSH access from: "
							for host in $hosts ; do
								$IPCHAINS -A user_fw -s $host -d 0/0 22 -p tcp $ACC_FLAG -j ACCEPT
								$IPCHAINS -A user_fw -s $host -d 0/0 22 -p udp $ACC_FLAG -j ACCEPT
								echo -n "$host "
							done
							echo ""
						fi
					fi

					# open access to HTTP server
					if test "$FW_WWWSERVER" != "" ; then
						echo -n "  Open HTTP access to: "
						for host in $FW_WWWSERVER ; do
							$IPCHAINS -A user_fw -s 0/0 -d $host 80 -p tcp $ACC_FLAG -j ACCEPT
							echo -n "$host "
						done
						echo ""
					fi

					# open access to SSL server
					if test "$FW_SSLSERVER" != "" -a "$FW_SSLPORT" != "" ; then
						echo -n "  Open SSL access on port $FW_SSLPORT to: "
						for host in $FW_SSLSERVER ; do
							$IPCHAINS -A user_fw -s 0/0 -d $host $FW_SSLPORT -p tcp $ACC_FLAG -j ACCEPT
							echo -n "$host "
						done
						echo ""
					fi

					# open access to FTP server
					if test "$FW_FTPSERVER" != "" ; then
						echo -n "  Open FTP access to: "
						for host in $FW_FTPSERVER ; do
							$IPCHAINS -A user_fw -s 0/0 -d $host  20 -p tcp $ACC_FLAG -j ACCEPT
							$IPCHAINS -A user_fw -s 0/0 -d $host  21 -p tcp $ACC_FLAG -j ACCEPT
							$IPCHAINS -A user_fw -s 0/0 -d $host  20 -p udp $ACC_FLAG -j ACCEPT
							echo -n "$host "
						done
						echo ""
					fi

					# open access to mail server
					if test "$FW_MAILSERVER" != "" ; then
						echo -n "  Open SMTP access to: "
						for host in $FW_MAILSERVER ; do
							$IPCHAINS -A user_fw -s 0/0 -d $host 25 -p tcp $ACC_FLAG -j ACCEPT
							echo -n "$host "
						done
						echo ""
					fi

					# let newsfeeds access the local news server
					if test "$FW_NNTPSERVER" != "" -a "$FW_NEWSFEED" != "" ; then
						echo -n "  Access to NNTP port from $FW_NEWSFEED to: "
						for feed in $FW_NEWSFEED ; do
							for  host in $FW_MAILSERVER ; do
								$IPCHAINS -A user_fw -s 0/0 -d $host 119 -p tcp $ACC_FLAG -j ACCEPT
								echo -n "$host "
							done
						done
						echo ""
					fi 

					# open access to name server
					if test "$FW_DNSSERVER" != "" ; then
						echo -n "  Open DNS access to: "
						for host in $FW_DNSSERVER ; do
							$IPCHAINS -A user_fw -s 0/0 -d $host 42 -p tcp $ACC_FLAG -j ACCEPT
							$IPCHAINS -A user_fw -s 0/0 -d $host 42 -p udp $ACC_FLAG -j ACCEPT
							$IPCHAINS -A user_fw -s 0/0 -d $host 53 -p tcp $ACC_FLAG -j ACCEPT
							$IPCHAINS -A user_fw -s 0/0 -d $host 53 -p udp $ACC_FLAG -j ACCEPT
							echo -n "$host "
						done
						echo ""
					fi

					# Transparent proxy for incoming traffic
					if test "$FW_TRANSPROXY_IN" != "" ; then
						echo "  Load transparent proxy (incoming traffic)"
						for i in $FW_TRANSPROXY_IN ; do
							PARAM=`echo $i | $AWK 'BEGIN {FS=","} {printf ( "-s %s -d %s %s -j REDIRECT %s", $1, $2 ,$3 ,$4 ) }'`
							${IPCHAINS} -A user_fw ${ACC_FLAGS} -p tcp ${PARAM} 
							${IPCHAINS} -A user_fw ${ACC_FLAGS} -p udp ${PARAM} 
						done
					fi



					# deny all packages which haven't been handled by one of the
					# other rules and are in the forbidden portrange
					echo -n "  Locked UDP ports: "
					for ports in $FW_UDP_LOCKED_PORTS ; do
						$IPCHAINS -A user_fw -s 0/0 -d 0/0 $ports -p udp $DENY_FLAG -j DENY
						echo -n "$ports "
					done
					echo ""

					echo -n "  Locked TCP ports: "
					for ports in $FW_TCP_LOCKED_PORTS ; do
						$IPCHAINS -A user_fw -s 0/0 -d 0/0 $ports -p tcp $DENY_FLAG -j DENY
						echo -n "$ports "
					done
					echo ""

					# after all rules are set up, we can replace the general denial 
					# with a general accept
					$IPCHAINS -D user_fw -s 0/0 -d 0/0 $DENY_FLAG -j DENY 
					$IPCHAINS -A user_fw -s 0/0 -d 0/0 $ACC_FLAG -j ACCEPT

					echo "Filters for incoming traffic loaded"

					# now take care of outgoing traffic

					# Transparent proxy for incoming traffic
					if test "$FW_TRANSPROXY_OUT" != "" ; then
						echo "  Load transparent proxy (outgoing traffic)"
						for i in $FW_TRANSPROXY_OUT ; do
							PARAM=`echo $i | $AWK 'BEGIN {FS=","} {printf ( "-s %s -d %s %s -j REDIRECT %s", $1, $2 ,$3 ,$4 ) }'`
							${IPCHAINS} -A user_out ${ACC_FLAGS} -p tcp ${PARAM} 
							${IPCHAINS} -A user_out ${ACC_FLAGS} -p udp ${PARAM} 
						done
					fi

					if test "$FW_INOUT" = "yes" -a -f $INOUT ; then
						# set up rules in new chain
						hosts=`${AWK} '/^#/ { next; } { print $1 }' $INOUT`
						if test "$hosts" != "" ; then
							echo -n "  Outbound traffic is OK from: "
							for host in $hosts ; do
								$IPCHAINS -A user_out -s $host -d 0/0 $ACC_FLAG -j ACCEPT
								echo -n "$host "
							done
							echo ""
						fi
					# block everything which hasn't been accepted so far
					$IPCHAINS -A user_out -s 0/0 -d 0/0 $DENY_FLAG -j DENY
					else
						echo "  No restrictions for outbound traffic"
					fi

					echo "Filters for outgoing traffic loaded"
				fi
			fi
			touch /var/lock/subsys/firewall
			success "start"
			;;

		stop)
			# check for dynamic addresses
			dynip

			if test "$FW_START" = "yes" ; then 
				echo -n "Shutting down $VERSION "
				if `cat /proc/net/ip_fwnames | $GREP user_fw > /dev/null` ; then
					# remove rules pointing to the firewall chain from the input rules
					for net in $FW_LOCALNETS ; do
						for dev in $FW_WORLD_DEV ; do
							$IPCHAINS -D input -s 0/0 -d $net -i $dev -j user_fw
						done
					done

					# now delete the chain
					$IPCHAINS -F user_fw
					$IPCHAINS -X user_fw
					success "stop"
				else
					failure "stop"
					echo -n "Chain user_fw does not exist, firewall not active?"
				fi

				# remove filter for outbound traffic
				if test "$FW_INOUT" = "yes" -o "$FW_TRANSPROXY_OUT" != "" ; then
					# remove rules from input chain
					for net in $FW_LOCALNETS ; do
						for dev in $FW_INT_DEV ; do
							$IPCHAINS -D input -s $net -d 0/0 -i $dev -j user_out
						done
					done
					$IPCHAINS -F user_out
					$IPCHAINS -X user_out
				fi
			fi
			rm -f /var/lock/subsys/firewall

			;;

		restart|reload)
			$0 stop  &&  $0 start  ||  failure "restart"
			;;

		status|list)
			if `cat /proc/net/ip_fwnames | $GREP user_fw > /dev/null` ; then
				echo "Firewall settings:"
				$IPCHAINS -L input -v -n
				$IPCHAINS -L user_fw -v -n
				if test "$FW_INOUT" = "yes" ; then
					$IPCHAINS -L user_out -v -n
				fi
			else
				echo "Firewall not active."
			fi
			exit 0
			;;

		*)
			echo ""
			echo "Usage: $0 {start|stop|status|restart|reload}"
			exit 1
			;;
	esac
else
	echo -n "Kernel lacks ipchains or forwarding support - firewall not enabled"
	failure "setup"
fi

# Inform the caller not only verbosely and set an exit status.
echo
exit 0


