#!/bin/bash
#echo First entry into mason-decide!
#Copyright 1999-2000, William Stearns <wstearns@pobox.com>.
#Given ipchains rules on stdin, allow the user to decide what to do with the rule.
#- Script shouldn't care about the source of the rules.  mgt feeds it a copy of newrules, then starts learning.
#- Originally put in place with same newrulepolicy
#- Feed mason output to this app. stderr?  fed to this app or not?

#stdin: rule input
#stdout: send to user
#&10: user terminal input
#&11: accepted rules output
#&12: postponed rules output
#&13: mason's flag file output for exit (this script feeds !!EXIT!! to that file when user chooses quit.
#cat indata | ./mason-decide 10<&1 11>accepted 12>postponed 13>flagfile

#FIXME - ignore line w/ only whitespace
#FIXME - should we be loading MASONLIB/CONF/running checksys, etc? almost certainly yes.
#FIXME - use wrap?
#Last YYYY code used: 0004, use 0005 next


MASONDIR=${MASONDIR:-"/var/lib/mason/"}
MASONCONF=${MASONCONF:-"/etc/masonrc"}
MASONLIB=${MASONLIB:-"${MASONDIR}masonlib"}
#MASONLIB=${MASONLIB:-"${MASONDIR}masonlib"}	#My editor of choice (mcedit) gets quoting backwards if there is an odd number of these.

if [ -f $MASONLIB ]; then
	. $MASONLIB
else
	#Can't use wrap here - no library to load it from.
	echo Missing $MASONLIB library file.  Please get a complete copy of Mason from >/dev/stderr
	echo http://www.pobox.com/~wstearns/mason/ .  Exiting. >/dev/stderr
	sleep 10
	exit 1
fi


EOFREACHED=''

mdcleanup () {
	#FIXME - this doesn't seem to catch before we get at least one rule...
	EXITNOW="YES"
	showstate 'mason-decide: exiting'
	if [ -n "$CURRLINE" ]; then
		echo "$CURRLINE" >&12
	fi
	echo '!!EXIT!!' >&13
	if type -path logger >/dev/null ; then	#Give something in /var/log/messages to give Mason a kick through the last rule...
		logger 'Mason-decide exiting'
	fi
	COMMITTED="YES"

	if [ "$EOFREACHED" != "YES" ]; then
		while read INLINE ; do	#postpone the rest
			echo "$INLINE" >&12
		done
		EOFREACHED="YES"
	fi

	echo
	echo $0 done!
	IFS="$ORIGIFS"
	exit 0
}

catchall
trap mdcleanup SIGINT		#Ctrl-C generates this

#Problems:
#Doesn't honor commentchar setting.
#Doesn't deal with a mix of ipchains/ipfwadm.
#Doesn't handle iptables at all.
#Rules added by $0 - type header >>&11 ?
#Add port to /etc/services and delete for rescan

menu () {
	echo $ENH "Modify choices:"
	echo $ENH "  ${KEY}E${NORM}dit        manually ${KEY}E${NORM}dit the rule"
	echo $ENH "  ${KEY}J${NORM}ot         ${KEY}J${NORM}ot a note at the end of the rule"		#append to the end with comment character
	echo $ENH "  ${KEY}A${NORM}ccept      change policy to ${KEY}A${NORM}ccept and commit"		#place these three in baserules
	echo $ENH "  ${KEY}D${NORM}eny        change policy to ${KEY}D${NORM}eny and commit"
	echo $ENH "  ${KEY}M${NORM}asq        change policy to ${KEY}M${NORM}asquerade and commit"
	echo $ENH "  ${KEY}R${NORM}eject      change policy to ${KEY}R${NORM}eject and commit"
	echo $ENH "Commit choices:"
	echo $ENH "  ${KEY}P${NORM}ostpone    ${KEY}P${NORM}ostpone choice"						#Append to newrules
	echo $ENH "  ${KEY}T${NORM}hrow away  ${KEY}T${NORM}hrow away line"
	echo $ENH "  ${KEY}B${NORM}lockedhost make this host a ${KEY}B${NORM}LOCKEDHOST and delete the rule"
	echo $ENH "  ${KEY}N${NORM}oincoming  make this port a ${KEY}N${NORM}OINCOMING port and delete the rule"
	echo $ENH "  ${KEY}C${NORM}ommit      ${KEY}C${NORM}ommit to the permanent firewall set"
	echo $ENH "  ${KEY}Q${NORM}uit        postpone any remaining rules and ${KEY}Q${NORM}uit"	#Feed !!EXIT!! to flag file, postpone rest of the rules
	#Postpone everything without exiting?
	#Original - go back to original version
	echo
} #End of menu

reppolicy () {
#Replace the policy in CURRLINE with $1
	if [ -n "$CURRLINE" ]; then
		case "$1" in
		[Aa]*)
			CURRLINE=`echo "$CURRLINE" | \
			sed -e 's/\(.*\)ipchains\(.*\)-j [A-Z]*\(\W.*\)/\1ipchains\2-j ACCEPT\3/' \
				-e 's/\(.*\)iptables\(.*\)-j [A-Z]*\(\W.*\)/\1iptables\2-j ACCEPT\3/' \
			    -e 's/\(.*\)ipfwadm\(.*\)-a [a-z]*\(\W.*\)/\1ipfwadm\2-a accept\3/' \
			    -e 's/\(.*\)ipfwadm\(.*\)\W-m\(\W.*\)/\1ipfwadm\2\3/'`
			;;
		[Dd]*)
			CURRLINE=`echo "$CURRLINE" | \
			sed -e 's/\(.*\)ipchains\(.*\)-j [A-Z]*\(\W.*\)/\1ipchains\2-j DENY\3/' \
				-e 's/\(.*\)iptables\(.*\)-j [A-Z]*\(\W.*\)/\1iptables\2-j DROP\3/' \
			    -e 's/\(.*\)ipfwadm\(.*\)-a [a-z]*\(\W.*\)/\1ipfwadm\2-a deny\3/' \
			    -e 's/\(.*\)ipfwadm\(.*\)\W-m\(\W.*\)/\1ipfwadm\2\3/'`
			;;
		[Mm]*)
			CURRLINE=`echo "$CURRLINE" | \
			sed -e 's/\(.*\)ipchains\(.*\)-j [A-Z]*\(\W.*\)/\1ipchains\2-j MASQ\3/' \
			    -e 's/\(.*\)iptables\(\W*-[AI]\)\W*[A-Za-z0-9]*\(.*\)-j [A-Z]*\(\W.*\)/\1iptables\2 POSTROUTING -t nat\3-j MASQUERADE\4/' \
			    -e 's/\(.*\)ipfwadm\(.*\)\W-m\(\W.*\)/\1ipfwadm\2\3/' \
			    -e 's/\(.*\)ipfwadm\(.*\)-a [a-z]*\(\W.*\)/\1ipfwadm\2-a accept -m\3/'`
			;;
		[Rr]*)
			CURRLINE=`echo "$CURRLINE" | \
			sed -e 's/\(.*\)ipchains\(.*\)-j [A-Z]*\(\W.*\)/\1ipchains\2-j REJECT\3/' \
			    -e 's/\(.*\)iptables\(.*\)-j [A-Z]*\(\W.*\)/\1iptables\2-j REJECT\3/' \
			    -e 's/\(.*\)ipfwadm\(.*\)-a [a-z]*\(\W.*\)/\1ipfwadm\2-a reject\3/' \
			    -e 's/\(.*\)ipfwadm\(.*\)\W-m\(\W.*\)/\1ipfwadm\2\3/'`
			;;
		esac
	fi
	#CURRLINE="$CURRLINE #$1"
} #End of reppolicy

if [ -f $MASONCONF ]; then		#The entire loadconf function is not required
    . $MASONCONF
fi
    
#checksys	#Disabled as we've already done this at least once in mason or mason-gui-text
checkconf


ORIGIFS="$IFS"
IFS=''
EXITNOW=''

echo 

#FIXME - null CURRLINE when committed; check for null CURRLINE.
echo $ENH Waiting for new rule, press ${KEY}Ctrl-C${NORM} to abort...
showstate 'mason-decide: waiting for new rule'
while [ "$EXITNOW" != "YES" ] && read INLINE ; do
	showstate 'mason-decide: processing rule'
	CURRLINE="$INLINE"
#Check to see if the line is a firewall rule
#Loop until some commit choice made.
	COMMITTED=''

	clear
	echo $ENH "${HEADER}-----------${NORM}"
	echo "$CURRLINE"
	echo $ENH "${HEADER}-----------${NORM}"
	menu
	while [ "$COMMITTED" != "YES" ] && read CHOICE <&10 ; do
		case "$CHOICE" in
		[Ee]*)
			echo Manually editing the rule.
			showstate 'mason-decide: editing rule'
			if ! type -path mktemp >/dev/null 2>/dev/null ; then
				TMPFILE="/var/lib/mason/manualedit"
				#FIXME - || RETVAL=$?   ?  likewise below.
				touch $TMPFILE
			else
				TMPFILE=`mktemp -q /tmp/mason-edit.XXXXXX`
			fi
			if [ $? -ne 0 ]; then
				echo $ENH "${WARN}$0: Can't create temp file.${NORM}"
			else
				echo "$CURRLINE" >$TMPFILE
				$EDITOR $TMPFILE <&10
				CURRLINE=`cat $TMPFILE`
				rm -f $TMPFILE || :
			fi	;;
		[Jj]*)
			echo Jot a note at the end of the rule, append to end with comment character
			echo $ENH Please enter a comment.  Just press ${KEY}Enter${NORM} if you changed your mind.
			read NEWCOMMENT <&10
			if [ -n "$NEWCOMMENT" ]; then
				CURRLINE="$CURRLINE $CMNT$NEWCOMMENT"
			fi	;;
		[Aa]*)
			echo Change policy to Accept and commit to baserules
			reppolicy 'Accept'
			echo "$CURRLINE" >&11
			COMMITTED="YES"	;;
		[Dd]*)
			echo Change policy to Deny and commit to baserules
			reppolicy 'Deny'
			echo "$CURRLINE" >&11
			COMMITTED="YES"	;;
		[Mm]*)
			echo Change policy to Masq and commit to baserules
			reppolicy 'Masq'
			echo "$CURRLINE" >&11
			COMMITTED="YES"	;;
		[Rr]*)
			echo Change policy to Reject and commit to baserules
			reppolicy 'Reject'
			echo "$CURRLINE" >&11
			COMMITTED="YES"	;;
		[Pp]*)
			echo Postpone choice, append to newrules
			echo "$CURRLINE" >&12
			COMMITTED="YES"	;;
		[Tt]*)
			echo Throw away line
			CURRLINE=''
			COMMITTED="YES"	;;
		[Bb]*)
			echo make this host a BLOCKEDHOST and delete the rule
			echo Please enter the host or network to block.
			echo Just press enter if you changed your mind.
			
			read TOBLOCK <&10
			if [ -n "$TOBLOCK" ]; then
				echo "BLOCKEDHOSTS=\"\${BLOCKEDHOSTS} $TOBLOCK\"" >>/etc/masonrc
				#Block right now.
				dorule i input '' '' '' "$TOBLOCK" '' '' '' '' '' deny '' "$LOGBLOCKS" '' || logfail $LINENO mason-decide: YYYY 0001
				dorule i output '' '' '' "$TOBLOCK" '' '' '' '' '' deny '' "$LOGBLOCKS" '' || logfail $LINENO mason-decide: YYYY 0002
				dorule i input '' '' '' '' '' "$TOBLOCK" '' '' '' deny '' "$LOGBLOCKS" '' || logfail $LINENO mason-decide: YYYY 0003
				dorule i output '' '' '' '' '' "$TOBLOCK" '' '' '' deny '' "$LOGBLOCKS" '' || logfail $LINENO mason-decide: YYYY 0004
				CURRLINE=''
				COMMITTED="YES"
			fi
			;;
		[Nn]*)
			echo make port a NOINCOMING port and delete the rule
			echo Please enter the port to make noincoming.
			echo Use the format \"portnumber/protocol\", such as
			echo \"telnet/tcp\" or \"23/tcp\".
			echo Just press enter if you changed your mind.
			read TONOINCOMING <&10
			if [ -n "$TONOINCOMING" ]; then
				echo "NOINCOMING=\"\${NOINCOMING} $TONOINCOMING\"" >>/etc/masonrc
				#BLOCK right now
				for OUTSIDEIF in $INCOMINGINTERFACES ; do
					oneblockproto $TONOINCOMING $OUTSIDEIF
				done
				CURRLINE=''
				COMMITTED="YES"
			fi
			;;
		[Cc]*)
			echo Commit to the permanent firewall set
			echo "$CURRLINE" >&11
			COMMITTED="YES"	;;
		[Qq]*)
			echo Quit.  When we exit the loop, any additional rules will be postponed
			EXITNOW="YES"
			if [ -n "$CURRLINE" ]; then
				echo "$CURRLINE" >&12		#Save current rule to postpone set
			fi
			echo '!!EXIT!!' >&13
			COMMITTED="YES"	;;
		esac
		if [ "$COMMITTED" != "YES" ]; then
			clear
			echo '-----------'
			echo "$CURRLINE"
			echo '-----------'
			menu
		fi
	done #If not committed and read choice
	echo $ENH Waiting for new rule, press ${KEY}Ctrl-C${NORM} to abort...
	if [ "`echo $INLINE | sed -e 's/#.*//' -e 's/ 	//g'`" != "`echo $CURRLINE | sed -e 's/#.*//' -e 's/ 	//g'`" ]; then	#If changed, remove old line and install new
		echo -n Rule changed, removing old rule...
		#REMOVEME
#echo	eval `echo $INLINE | sed -e "s/\(.*\)ipchains\(.*\)-[AI]\(\W*[A-Za-z0-9]*\)\(\W.*\)/\1ipchains\2-D\3$NOLOGSUFFIX\4/" \
#								 -e "s/\(.*\)iptables\(.*\)-[AI]\(\W*[A-Za-z0-9]*\)\(\W.*\)/\1iptables\2-D\3$NOLOGSUFFIX\4/" \
#								 -e 's/\(.*\)ipfwadm\(.*\)-[ai]\(\W.*\)/\1ipfwadm\2-d\3/'`
		eval `echo $INLINE | sed -e "s/\(.*\)ipchains\(.*\)-[AI]\(\W*[A-Za-z0-9]*\)\(\W.*\)/\1ipchains\2-D\3$NOLOGSUFFIX\4/" \
								 -e "s/\(.*\)iptables\(.*\)-[AI]\(\W*[A-Za-z0-9]*\)\(\W.*\)/\1iptables\2-D\3$NOLOGSUFFIX\4/" \
								 -e 's/\(.*\)ipfwadm\(.*\)-[ai]\(\W.*\)/\1ipfwadm\2-d\3/'`		# || : ? Sigh.
		if [ -n "$CURRLINE" ]; then
			echo -n and installing new version...
			#the "N" after \3 on the iptables and ipchains lines was removed so the new rule will be added to the main chain and not the NOLOG chain.
#echo 		eval `echo $CURRLINE | sed -e 's/\(.*\)ipchains\(.*\)-[AI]\(\W*[A-Za-z]*\)\(\W.*\)/\1ipchains\2-I\3\4/' \
#									   -e 's/\(.*\)iptables\(.*\)-[AI]\(\W*[A-Za-z]*\)\(\W.*\)/\1iptables\2-I\3\4/' \
#									   -e 's/\(.*\)ipfwadm\(.*\)-[ai]\(\W*[a-z]*\)\(\W.*\)/\1ipfwadm\2-i\3\4/'`
	 		eval `echo $CURRLINE | sed -e 's/\(.*\)ipchains\(.*\)-[AI]\(\W*[A-Za-z]*\)\(\W.*\)/\1ipchains\2-I\3\4/' \
									   -e 's/\(.*\)iptables\(.*\)-[AI]\(\W*[A-Za-z]*\)\(\W.*\)/\1iptables\2-I\3\4/' \
									   -e 's/\(.*\)ipfwadm\(.*\)-[ai]\(\W*[a-z]*\)\(\W.*\)/\1ipfwadm\2-i\3\4/'`
		fi
		echo done!
	fi
	CURRLINE=''
	showstate 'mason-decide: waiting for new rule'
done #not EXITNOW and read INLINE
if [ "$EXITNOW" != "YES" ]; then
	EOFREACHED="YES"
fi

if [ "$EOFREACHED" != "YES" ]; then
	while read INLINE ; do	#postpone the rest
		echo "$INLINE" >&12
	done
fi

echo
echo $0 done!
showstate ''
IFS="$ORIGIFS"


