#!/bin/sh
# @vasm : vfirewall
# @level: root
# @description: Set up a firewall
# 
# (c) Eko M. Budi, 2003
# (c) Vector Linux, 2003
#
# Released under GNU GPL

vdir=$(dirname $0)
source $vdir/vasm-functions

check_root

# Properties
rc_firewall="/etc/rc.d/rc.firewall"

rc_prefix="/etc/rc.d/rc."
inet_prefix="${rc_prefix}inet"
inet_files="${inet_prefix}?"


mk_rc_firewall ()
{

cat <<'EOF'
#!/bin/sh
# firewall
# This is a simple firewall rules to setup a masquerading 
# gateway (Internet sharing), with the following configuration 
#
# {RED}-----[firewall]------{GREEN}
#
# RED   = The Internet
# GREEN = Your Intranet 
#
# Sufficient for home use, serving some casual clients.
# Not for a serious office !!!
# You cannot sue me for whatever reason regarding this script.
#
# This firewall uses network address based rules.
# Therefore it is independent to interface, and easier to debug.
#
# GNU GPL (c) Eko M. Budi, 2004
#         (c) Vector Linux, 2004

# The Internal Network you want to protect.
# Must be an internal networks.
# Empty means no masquerading
GREEN_NET=""

# The traffic that can pass from GREEN the RED network (internet).
# see /etc/services for ports definition
# ALL means open all ports, space separated
PORT_OUT="domain http https pop3 pop3s imap ssh ftp ftp-data irc"
ICMP_OUT="3 8 11"

# The open ports of THIS host.
# ALL means all ports, space separated
PORT_IN="domain ssh http https"
ICMP_IN="0 3 8 11"

########################################################################
# Do the business ...
. /etc/rc.d/functions

########################################################################
# here we go now
IPT="/usr/sbin/iptables"
MODPROBE="/sbin/modprobe"

## load modules
$MODPROBE ip_tables
$MODPROBE iptable_filter
$MODPROBE iptable_nat
$MODPROBE ip_nat_ftp  
$MODPROBE ip_nat_irc 
$MODPROBE ip_conntrack  
$MODPROBE ip_conntrack_ftp  
$MODPROBE ip_conntrack_irc  

# Check if a net is an internal network
is_internal()
{
   IPADDR=$(echo $1 | cut -f 1 -d /)

   NET=$(ipmask 255.255.0.0 $IPADDR | cut -f 2 -d ' ')
   [ "$NET" = "192.168.0.0" ] && return

   NET=$(ipmask 255.240.0.0 $IPADDR | cut -f 2 -d ' ')
   [ "$NET" = "172.16.0.0" ] && return

   NET=$(ipmask 255.0.0.0 $IPADDR | cut -f 2 -d ' ')
   [ "$NET" = "10.0.0.0" ] && return

   false
}

firewall_disable_forwarding()
{
  echo "0" > /proc/sys/net/ipv4/ip_forward 
}

firewall_enable_forwarding()
{
  echo "1" > /proc/sys/net/ipv4/ip_forward
  # Enable IP spoofing protection, turn on Source Address Verification
  for f in /proc/sys/net/ipv4/conf/*/rp_filter; do
     echo 1 > $f
  done
  # Disable ICMP Redirect Acceptance
  for f in /proc/sys/net/ipv4/conf/*/accept_redirects; do
     echo 0 > $f
  done
}

firewall_clear()
{
  ## Clear all rules
  $IPT -F 
  $IPT -F -t nat 
  $IPT -F -t mangle 
  $IPT -X 
  $IPT -P INPUT ACCEPT
  $IPT -P FORWARD DROP
  $IPT -P OUTPUT ACCEPT
}

# STANDARD RULES
firewall_default()
{
  $IPT -P INPUT DROP
  $IPT -P FORWARD DROP
  $IPT -P OUTPUT ACCEPT

  $IPT -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
  $IPT -A FORWARD -f -j ACCEPT

  $IPT -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
  $IPT -A INPUT -f -j ACCEPT

  # Enabling Syn flood protection
  #$IPT -A INPUT -p tcp --syn -m limit ! --limit 1/s -j DROP
 
  # Enabling Furtive port scanner protection
  #$IPT -A INPUT -p tcp --tcp-flags SYN,ACK,FIN,RST RST -m limit ! --limit 1/s -j DROP
  
  # Enabling ping of death protection
  # $IPT -A INPUT -p icmp --icmp-type echo-request -m limit ! --limit 1/s -j DROP
}

# Rules for allowing traffic from the GREEN to the RED network
firewall_forward()
{
   ## skip if empty
   [ -z "$GREEN_NET" ] && return 0   

   if [ "$PORT_OUT" != "ALL" ]; then
     for PORT in $PORT_OUT; do
       $IPT -A FORWARD -s $GREEN_NET -p udp --dport $PORT -j ACCEPT
       $IPT -A FORWARD -s $GREEN_NET -p tcp --dport $PORT -j ACCEPT
     done
   else
     $IPT -A FORWARD -s $GREEN_NET -p all -j ACCEPT
   fi

   if [ "$ICMP_OUT" != "ALL" ]; then
     for TYPE in $ICMP_OUT; do 
       $IPT -A FORWARD -s $GREEN_NET -p icmp --icmp-type $TYPE -j ACCEPT
     done
   else
      $IPT -A FORWARD -s $GREEN_NET -p icmp -j ACCEPT
   fi
}

# Masquerade traffic from GREEN to YELLOW/RED
firewall_masquerade()
{
   ## skip if empty
   [ -z "$GREEN_NET" ] && return

   if is_internal $GREEN_NET; then
     $IPT -t nat -A POSTROUTING -s $GREEN_NET -d ! $GREEN_NET -j MASQUERADE
   fi
}

# Rules for accepting input to this gateway
firewall_input()
{
  # Accept from myself
  $IPT -A INPUT -i lo -j ACCEPT 

  # Accept from GREEN_NET
  if [ "$GREEN_NET" ]; then
     $IPT -A INPUT -s $GREEN_NET -j ACCEPT
  fi
  
  ## Everything else must be filtered
  if [ "$PORT_IN" != "ALL" ]; then
     for PORT in $PORT_IN ; do 
       $IPT -A INPUT -p udp --dport $PORT -j ACCEPT
       $IPT -A INPUT -p tcp --dport $PORT -j ACCEPT
     done
  else
     $IPT -A INPUT -p all -j ACCEPT
  fi

  if [ "$ICMP_IN" != "ALL" ]; then
     for TYPE in $ICMP_IN ; do 
       $IPT -A INPUT -p icmp --icmp-type $TYPE -j ACCEPT
     done
  else
     $IPT -A INPUT -p icmp -j ACCEPT
  fi
}


## START IT
firewall_start() {
  firewall_disable_forwarding
  firewall_clear
  firewall_default

  firewall_input
  firewall_masquerade  
  firewall_forward
  
  firewall_enable_forwarding  
  return 0
}

firewall_stop()
{
  firewall_disable_forwarding
  firewall_clear
  return 0
}

case "$1" in
  start)
    echo -n "Starting firewall ..."
    firewall_start
    evaluate_retval
    ;;
  stop)
    echo -n "Stopping firewall ..."
    firewall_stop
    evaluate_retval
    ;;
  reload)
    echo -n "Reloading firewall ..."
    firewall_start
    evaluate_retval
    ;;  
  restart)
    $0 stop
    sleep 2
    $0 start
    ;;
  status)
    $IPT -L
    echo
    $IPT -t nat -L
    ;;
  *)  
    echo "Usage $0 {start|stop|restart|reload|status}"
esac

EOF

}

# Get the inet values from file
function get_inet()
{
  DEVICE=""
  NETNAME=""
  IPADDR=""
  NETMASK=""
  GATEWAY=""
  PROBE="no"
  inet_file=$1
  eval `grep -e '^DEVICE=' $inet_file`
  eval `grep -e '^IPADDR=' $inet_file`
  eval `grep -e '^NETMASK=' $inet_file`
  eval `grep -e '^GATEWAY=' $inet_file`
  eval `grep -e '^DHCP=' $inet_file`
  eval `grep -e '^PROBE=' $inet_file`
}

function build_inet_menu()
{
   count=0
   for file in $inet_files; do
     if [ -x $file ]; then
       get_inet $file
       inet=${file##${rc_prefix}}
       if [ "$DHCP" != "yes" ]; then
	  echo "$inet \"DEV=$DEVICE, IP=$IPADDR/$NETMASK\" \\"
       fi    	
       let count++
     fi
   done
   if [ $count == 0 ]; then
     errorbox "No network connection. Add one first, mate !"
     clean_exit 1 
   fi
}

# Check if a net is an internal network
is_internal()
{
   IPADDR=$(echo $1 | cut -f 1 -d /)

   NET=$(ipmask 255.255.0.0 $IPADDR | cut -f 2 -d ' ')
   [ "$NET" = "192.168.0.0" ] && return

   NET=$(ipmask 255.240.0.0 $IPADDR | cut -f 2 -d ' ')
   [ "$NET" = "172.16.0.0" ] && return

   NET=$(ipmask 255.0.0.0 $IPADDR | cut -f 2 -d ' ')
   [ "$NET" = "10.0.0.0" ] && return

   false
}


PORT_LIST="ssh domain http https 8080 ftp ftp-data smtp smtps pop3 pop3s imap
nntp irc sunrpc nfsd lockd netbios-ns netbios-dgm netbios-ssn swat xdmcp 6000:6063"

# print menu
port_desc()
{
case $1 in
  ssh) echo 	"Secure Shell Login" ;;
  domain) echo 	"Domain Name Server";;  
  http) echo 	"World Wide Web HTTP";;
  https) echo 	"HTTP secure";;
  8080) echo 	"HTTP-proxy,servlet,JSP";;
  ftp) echo 	"File Transfer Protocol";;
  ftp-data) echo "File Transfer [Default Data]";;
  smtp) echo 	"Simple Mail Transfer";;
  smtps) echo 	"SMTP secure";;
  pop3) echo     "Post Office Protocol";;
  pop3s) echo    "POP3 secure";;
  imap) echo	 "Interim Mail Access Protocol";;
  nntp) echo     "Network News Transfer Protocol";;
  irc ) echo     "Internet Relay Chat Protocol";;
  sunrpc) echo	 "SUN Remote Procedure Call";;
  nfsd) echo     "NFS server daemon";;
  lockd) echo    "NFS lock daemon";;
  netbios-ns) echo	"Samba NETBIOS Name Service";;
  netbios-dgm) echo	"Samba NETBIOS Datagram Service";;
  netbios-ssn) echo	"Samba NETBIOS Session Service";;
  swat) echo		"SAMBA web configuration tool";;
  xdmcp) echo		"Display Manager Control Protocol";;
  "6000:6063") echo	"X Window System";;
esac
}

# print ON if $1 in $OPEN_PORTS
port_state()
{
   if echo $OPEN_PORTS | grep -qw $1 ; then
      echo on   
   else
      echo off
   fi
}

get_firewall()
{
  GREEN_NET=""
  TCP_OUT=""
  TCP_IN=""
  
  eval `grep -e '^GREEN_NET=' $rc_firewall`
  eval `grep -e '^PORT_OUT=' $rc_firewall`
  eval `grep -e '^PORT_IN=' $rc_firewall`
}

set_firewall()
{
  cat $rc_firewall | sed "
  s#^GREEN_NET=.*#GREEN_NET='$GREEN_NET'# 
  s#^PORT_OUT=.*#PORT_OUT='$PORT_OUT'# 
  s#^PORT_IN=.*#PORT_IN='$PORT_IN'#" > $rc_firewall
}

menuA1A() {
  if [ ! -f $rc_firewall ]; then
    infobox "No firewall script. Creating a new one"
    mk_rc_firewall > $rc_firewall
  fi

TITLE="OPEN HOST PORTS"
TEXT="\nSelect network ports of this host that you want to open.\n
Sorry, the list is limited. If you want more than this list,\n
then you are an expert and should write your own firewall script ;-)."
DIMENSION="20 78 10"

OPEN_PORTS=$PORT_IN

dbug open=$OPEN_PORTS
echo '$DCMD --backtitle "$BACKTITLE" --title "$TITLE" --checklist "$TEXT" $DIMENSION \' > $fmenu
for port in $PORT_LIST; do
  desc=$(port_desc $port)
  state=$(port_state $port)
  echo $port \"$desc\" $state \\ >> $fmenu
done
echo ' 2> $freply ' >> $fmenu

#cat $fmenu

. $fmenu

status=$?
[ $status != 0 ] && return $status

if [ "$CMD" ]; then
  PORT_IN=$(cat $freply | sed -e 's!/! !g')
else
  PORT_IN=$(cat $freply | sed -e 's/"//g')
fi

}

menuA1B()
{
   infobox "Saving firewal configuration"
   set_firewall
   sleep 2
   clean_exit 0
}


menuA2A()
{

while [ 0 ]; do
DIMENSION="18 60 4"
TITLE="GATEWAY CONFIGURATION"
TEXT="\n
This menu allows you to setup this host as a gateway,\n
possibly for an internet sharing server. You should\n
have INTERNET and INTRANET connections like this:\n
  {INTERNET}---[gateway]---{INTRANET}\n\n

Now tell me which connection is the INTRANET side:"

  echo '$DCMD --backtitle "$BACKTITLE" --title "$TITLE" --menu "$TEXT" $DIMENSION \' > $fmenu
  build_inet_menu >> $fmenu
  echo '2> $freply' >> $fmenu

#  cat $fmenu
  source $fmenu
  
  status=$?
  [ $status != 0 ] && return $status

  inet=$(cat $freply)
  inet_file="${rc_prefix}$inet"
  get_inet $inet_file
  
  if is_internal $IPADDR; then
     return 0     
  fi  

  yesnobox "IP $IPADDR in not an internal network. Use it anyway ?"
  [ $? = 0 ] && return 0

done
}


menuA2B()
{

  GREEN_NET=$(ipmask $NETMASK $IPADDR | cut -f 2 -d ' ')"/$NETMASK"

DIMENSION="20 70 10"
TITLE="GATEWAY CONFIGURATION"
TEXT="\n
You have chosen $inet as the intranet side. The network is:\n
   $GREEN_NET\n
Next, select the ports that are allowed to pass this gateway."

OPEN_PORTS=$PORT_OUT

dbug open=$OPEN_PORTS
echo '$WCMD --backtitle "$BACKTITLE" --title "$TITLE" --checklist "$TEXT" $DIMENSION \' > $fmenu
for port in $PORT_LIST; do
  desc=$(port_desc $port)
  state=$(port_state $port)
  echo $port \"$desc\" $state \\ >> $fmenu
done
echo ' 2> $freply ' >> $fmenu

# cat $fmenu

. $fmenu

status=$?
[ $status != 0 ] && return $status

if [ "$CMD" ]; then
  PORT_OUT=$(cat $freply | sed -e 's!/! !g')
else
  PORT_OUT=$(cat $freply | sed -e 's/"//g')
fi
}

menuA2C()
{
TITLE="DONE"
TEXT="\n
The gateway portion has been setup.\n
For internet sharing to work, you should:\n
- enable DNS service (dnsmasq) on this host\n
- set the GATEWAY and DNS of the clients to\n
  this host (IP=$IPADDR).\n
Enjoy .... !!!"
DIMENSION="12 60"

$DCMD --backtitle "$BACKTITLE" --title "$TITLE" --msgbox "$TEXT" $DIMENSION
  
   clean_exit 0
}

#########################################
# Open ports
menu_open()
{
  
  list_ports
  infobox "Creating a new firewall script"
  if [ -f $rc_firewall ]; then
    cp $rc_firewall ${rc_firewall}.backup
  fi
  mk_rc_firewall > $rc_firewall
  chmod a+x $rc_firewall
  sleep 3
  return 0
}

#########################################
# Create a new firewall script
menu_new()
{
  infobox "Creating a new firewall script"
  if [ -f $rc_firewall ]; then
    cp $rc_firewall ${rc_firewall}.backup
  fi
  mk_rc_firewall > $rc_firewall
  chmod a+x $rc_firewall
  sleep 3
  return 0
}

#########################################
# start firewall
menu_start()
{
  if [ ! -x $rc_firewall ]; then
    errorbox "No firewall script. Create a new one first"
    return 1
  fi
  infobox "Starting the firewall"
  $rc_firewall start &> /dev/null
  sleep 2
  msgbox "Firewall has been started.\nCheck it from a terminal using command 'iptables -L'" "INFO"
  return 0
}

#########################################
# stop firewall
menu_stop()
{
  if [ ! -x $rc_firewall ]; then
    errorbox "No firewall script. Create a new one first"
    return 1
  fi
  infobox "Stopping the firewall"
  $rc_firewall stop &> /dev/null
  sleep 2
  return 0
}

#########################################
# enable firewall
menu_enable()
{
  infobox "Enabling the firewall"
  if [ ! -f $rc_firewall ]; then
    mk_rc_firewall > $rc_firewall
  fi
  chmod a+x $rc_firewall
  sleep 2
  return 0
}

#########################################
# Disable firewall script
menu_disable()
{
  infobox "Disabling firewall script"
  if [ -x $rc_firewall ]; then
    $rc_firewall stop &> /dev/null
    chmod a-x $rc_firewall
  fi
  sleep 2
  return 0
}

##################################################
# Main Menu
menuA()
{
while [ 1 ]; do
TITLE="FIREWALL CONFIGURATION"
TEXT="\n
This configurator setups a simple firewall to protects this host and\n
enable internet sharing. You should create a NEW firewall, then use \n
the OPEN and GATEWAY menu. After that you can start, stop, enable and\n
disable the firewall at any time. Please DISABLE this firewall if you\n
are using other firewall such as GShield or portsentry."

DIMENSION="21 74 8"

$DCMD --backtitle "$BACKTITLE" --title "$TITLE" --menu "$TEXT" $DIMENSION \
  "NEW"     "create a new firewall" \
  "OPEN"    "open some ports of this host" \
  "GATEWAY" "setup this host as a gateway" \
  "START"   "Start the firewall" \
  "STOP"    "Stop the firewall" \
  "ENABLE"  "enable the firewall on booting" \
  "DISABLE" "disable firewall on booting" \
  "EXIT"    "exit this menu" \
2> $freply

status=$?
[ $status != 0 ] && return $status

case $(cat $freply) in
    ENABLE)
      menu_enable
      ;;
    DISABLE)
      menu_disable
      ;;
    START)
      menu_start
      ;;
    STOP)
      menu_stop
      ;;
    OPEN)
      $0 --open
      ;;
    GATEWAY)
      $0 --gateway
      ;;
    NEW)
      menu_new
      ;;
    EXIT)
      return 0
      ;;
esac
done

}

######################
#
case $1 in
  --open)
    get_firewall
    wizard menuA1A menuA1B
    ;;
  --gateway)
    get_firewall
    wizard menuA2A menuA2B menuA2C
    ;;
  *)
    wizard menuA
esac

clean_exit

