#!/bin/sh
# 2000.08.25 kjw - this script is called by both ppp and pcmcia to setup
# ipchains security
#
# USAGE: $0 (up|down) (internal|external) $DEVICE $IPLOCAL
# USAGE: $0 init
#
# Theory of operations:
# First, the ipchains policies, as set by /etc/rc.d/rc.local
# input   set to accept
# forward set to deny
# output  set to accept
#
# all input rules will be to DENY packets that I don't want. order is
# thus unimportant
#
# all forward rules will be MASQ for external and ACCEPT from internals.
# order is important; MASQ need to be inserted and ACCEPT appended


#########################################################################
## begin subroutines ##

IPC='/sbin/ipchains'

delete_external_ipchains()
{
# expects DEVICE, $IPLOCAL

	# Convenience defines:
	IF=$DEVICE

	for RULE in input forward output ; do
		RUL=`echo $RULE | cut -c1-3`

		# delete references to our chains
		echo "$IPC --delete $RULE --interface $IF --jump $IF-$RUL"
		      $IPC --delete $RULE --interface $IF --jump $IF-$RUL

		# delete the chains themselves
		echo "$IPC -F $IF-$RUL"
		      $IPC -F $IF-$RUL
		echo "$IPC -X $IF-$RUL"
		      $IPC -X $IF-$RUL
		done
}

create_external_ipchains()
{ # uses $IPC
# expects DEVICE, IPLOCAL
# IPLOCAL=ip address of the local interface
# DEVICE=interface name, i.e. ppp0, eth1

	delete_external_ipchains

	# Convenience defines:
	_DEST='--destination'
	_TCP="--protocol tcp"
	_UDP="--protocol udp"
	IF=$DEVICE

	for RULE in inp for out ; do
		# create or null==delete
		echo "$IPC --new-chain $IF-$RULE"
		      $IPC --new-chain $IF-$RULE
		done

	## NOTE: we don't need to include --interface $DEVICE because that
	## check is done BEFORE jumping to the $IF-{inp,for,out} rules

	# Only allow packets targeted at that interface
	echo "$IPC --append $IF-inp --destination ! $IPLOCAL/32 --jump DENY"
	      $IPC --append $IF-inp --destination ! $IPLOCAL/32 --jump DENY

	# Deny specific (local) services, since we still need
	# to allow privledged ports (<=1024)
	# 515 = lpr, 80 = http, 25 = smtp, 4321 = pmcd, 4330 = pmlogger ctrl
	# 21 = ftpd, 53 = named
	# use 'REJECT' instead of 'DENY' so that you get a Connection Refused,
	# just as if the service did not exist.
	for SERVICE in 515 80 25 4321 4330 21 53 ; do
		echo "$IPC --append $IF-inp $_DEST $IPLOCAL $SERVICE $_TCP --jump REJECT"
		      $IPC --append $IF-inp $_DEST $IPLOCAL $SERVICE $_TCP --jump REJECT
		done
	# reject udp services
	# 67 = bootp (dhcpd), 53 = named
	for SERVICE in 67 53 ; do
		echo "$IPC --append $IF-inp $_DEST $IPLOCAL $SERVICE $_UDP --jump REJECT"
		      $IPC --append $IF-inp $_DEST $IPLOCAL $SERVICE $_UDP --jump REJECT
		done
	unset SERVICE

	# everything going out an external if should be masq'ed to that addr anyways
	echo "$IPC --append $IF-for --jump MASQ"
	      $IPC --append $IF-for --jump MASQ

	# Now that our personal chains are created, attach them to the real chains

	# deny obviously bad packets first. only packets for that if are allowed,
	# since it's a masquerading interface.
	echo "$IPC --insert input 1   --interface $IF --jump $IF-inp"
	      $IPC --insert input 1   --interface $IF --jump $IF-inp

	# since we're masquerading, we should masq first before hitting internal
	# "internal, so just fwd it as is".
	echo "$IPC --insert forward 1 --interface $IF --jump $IF-for"
	      $IPC --insert forward 1 --interface $IF --jump $IF-for

	# ... no output rules? ...
	#echo "$IPC --append output    --interface $IF --jump $IF-out"
	#      $IPC --append output    --interface $IF --jump $IF-out

}

do_internal_ipchains()
{ # uses $IPC
# expects _APPEND, _INSERT, _INSERT_POSITION to be defined
# and IPLOCAL, DEVICE

	NET=`ipcalc --network $IPLOCAL 0xffffff00 | cut -f2 -d=`

	# Convenience defines:
	_DENY='--jump DENY'
	_MASQ='--jump MASQ'
	_ACCE='--jump ACCEPT'
	_TCP="--protocol tcp"
	_IF="--interface $DEVICE"

	# allow internal packets to be routed around
	echo "$IPC $_APPEND forward $_IF --source 10.0.0.0/8 --destination 10.0.0.0/8 $_ACCE"
	      $IPC $_APPEND forward $_IF --source 10.0.0.0/8 --destination 10.0.0.0/8 $_ACCE

	# allow only packets from the internal interface (leaf, not transit network)
	echo "$IPC $_APPEND input $_IF --source ! $NET/24 $_DENY"
	      $IPC $_APPEND input $_IF --source ! $NET/24 $_DENY
}

## end subroutines ##
#########################################################################

exec >/tmp/ipchains_config.$3.log
exec 2>&1

## parse and verify command-line

MODE=$1
echo "Mode $MODE"

if [ "$MODE" != 'init' -a "$MODE" != 'up' -a "$MODE" != 'down' ] ; then
	echo "ERROR: Mode [$MODE] is none of 'init', 'up' or 'down'"
	exit 5
	fi

if [ "$MODE" = 'init' ] ; then
	$IPC -P input   ACCEPT
	$IPC -P forward DENY
	$IPC -P output  ACCEPT

	# set tcp connections to timeout in 6000 seconds (100 minutes) instead
	# of the default 15 minutes. leave tcp-fin and udp unchanged.
	/sbin/ipchains -M -S 6000 0 0
	exit 0
	fi


TYPE=$2
DEVICE=$3
IPLOCAL=$4
echo "Type $TYPE"
echo "Interface $DEVICE"
echo "IP address $IPLOCAL"
logger -t ipchains_config "$MODE IF=$DEVICE IP=$IPLOCAL"

if [ "$TYPE" != 'external' -a "$TYPE" != 'internal' ]
	then
	echo "ERROR: Type [$TYPE] is none of 'internal' or 'external'"
	exit 5
	fi

## do the down stuff
if [ "$MODE" = 'down' ] ; then
	_APPEND='--delete'
	_INSERT='--delete'
	_INSERT_POSITION=

	if [ "$TYPE" = 'external' ] ; then delete_external_ipchains
	else                               do_internal_ipchains ; fi

	exit 0
	fi

## else do the up stuff
if [ "$MODE" = 'up' ] ; then
	_APPEND='--append'
	_INSERT='--insert'
	_INSERT_POSITION=1

	if [ "$TYPE" = 'external' ] ; then create_external_ipchains
	else                               do_internal_ipchains ; fi

	exit 0
	fi

