#!/bin/bash
#
# Tue Oct 13 12:07:08 2009
#
# Ignore_ports() excluded.

version="0.65.e"

# SlackFire is an iptables script with a number of accompanying configuration 
# files to protect a Slackware Linux machine with one or two network interfaces
# and/or a small network with a stateful firewall.  See the README file for
# details.
#
# (C) Copyright 2005-2009 by Mikhail Zotov 
#
# Project homepage: http://slackfire.berlios.de/
# Old page:         http://slackfire.narod.ru/    (versions 0.41--0.43)
# E-mail:           slackfire@gmail.com
#
# Please, include "slackfire" in the subject line of your message.
#
###############################################################################
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for the details:
# http://www.gnu.org/licenses/licenses.html#GPL
#
###############################################################################

echoe() 
{
  echo -e $1
}

#
# Location of iptables
#

PATH=/usr/sbin:/bin

if [ ! -x /usr/sbin/iptables ] ; then
  echoe "SlackFire expects to find iptables in /usr/sbin\n"
  /bin/ls /usr/sbin/iptables
  echoe "\nPlease, edit the PATH variable at the top of the script or"
  echoe "create an appropriate link to point to the location of iptables."
  exit 1
fi

#
# Location of configuration files
#

CONFDIR="/etc/slackfire"

if [ -s $CONFDIR/slackfire.conf ] ; then
  . $CONFDIR/slackfire.conf
else
  echo "Dear $USER, $CONFDIR/slackfire.conf cannot be found."
  echo "Have you read the README file? ;-)"
  exit 1
fi

# This is a directory for files sourced from SlackFire.
#SHAREDIR="/usr/share/slackfire"
SHAREDIR="/usr/share/slackfire"
LIST="INPUT.OUTPUT.chains special.chains spec.functions tech.functions ipv4-sysctl.settings"
for i in $LIST ; do
  if [ ! -s $SHAREDIR/$i ] ; then
    echo "$SHAREDIR/$i not found!"
    exit 1
  fi
done
if [ "$ROUTER" = "1" ] ; then
  if [ ! -s $SHAREDIR/FORWARD.chain ] ; then
    echo "$SHAREDIR/FORWARD.chain not found"
    exit 1
  fi
fi

# This is a directory for temporary files. It must be writable
if [ ! "$TMP_FILE_DIR" ] ; then
  TMP_FILE_DIR="$CONFDIR"
fi

touch ${TMP_FILE_DIR}/test.file || \
  ( echo "Set TMP_FILE_DIR in slackfire.conf" ; exit 1 )
rm -f $TMP_FILE_DIR/test.file

#
# Just for fun :-)
#

RED=$'\e[31;01m'
GREEN=$'\e[32;01m'
YELLOW=$'\e[33;01m'
NORMAL=$'\e[0m'

###############################################################################
#
# START SlackFire
#

start_slackfire()
{

# A couple of functions to "automate" making messages.
# They can call echoe but I prefer to avoid recursive calls
#
# Messages that comment the start-up process
comment() {
  echo -e "\t\t$1"
}
# A new-line tabbed message (used in error mesages)
err_notice() { 
  echo -e "\n\t$1"
}
# A tabbed message
notice() { 
  echo -e "\t$1"
}

#
# A couple of checks before we actually do anything:
#

if [ ! $EXT_IF ] ; then
  err_notice "${RED}External interface is not defined!${NORMAL}\n"
  exit 1
fi

if [ "$NAT" = "1" ] ; then
  ROUTER=1
fi

if [ "$ROUTER" = "1" ] && [ ! $INT_IF ] && [ ! $TRUSTED_IF ] ; then
  err_notice "${RED}In your settings, INT_IF and TRUSTED_IF are both empty."
  notice "Set one of these variables if you need a router.${NORMAL}\n"
  exit 1
fi

if [ $INT_IF ] && [ "$INT_IF" = "$TRUSTED_IF" ] ; then
  err_notice "${RED}INT_IF and TRUSTED_IF point to one and the same device."
  notice "This makes no sense. See comments in slackfire.conf${NORMAL}\n"
  exit 1
fi

if [ "$EXT_IF" = "$TRUSTED_IF" ] ; then
  err_notice "${RED}EXT_IF and TRUSTED_IF point to one and the same device."
  notice "This makes no sense. See comments in slackfire.conf${NORMAL}\n"
  exit 1
fi

if [ "$EXT_IF" = "$INT_IF" ] ; then
  err_notice "${RED}EXT_IF and INT_IF point to one and the same device."
  notice "This makes no sense. See comments in slackfire.conf${NORMAL}\n"
  exit 1
fi

#
# Start-up message
#

echoe "\n${YELLOW}SlackFire${NORMAL} $version ${GREEN}is starting${NORMAL} \c"

#
# A number of short-hands and settings that can be tweaked if necessary
#

FORWARD="iptables -A FORWARD"
OUTPUT="iptables -A OUTPUT"
INPUT="iptables -A INPUT"

#STATE="-m state --state"
STATE="-m conntrack --ctstate"
LIMIT="-m limit --limit"

# Normal log rate
LOG_RATE="20/minute --limit-burst 1"
#LOG_RATE="30/minute --limit-burst 3"
LOG="LOG $LIMIT $LOG_RATE --log-prefix"

# Log rate for the case we are likely to be scanned
SCAN_LOG_RATE="30/minute --limit-burst 1"
LOG_SCAN="LOG $LIMIT $SCAN_LOG_RATE --log-prefix"

# This is a rate for allowed incoming ICMP connections.
ACCEPT_RATE="1/second --limit-burst 1"

# These two settings are used in syn-flood_protection chain
FLOOD_LOG_RATE="1/second --limit-burst 1"
SYN_FLOOD="10/second --limit-burst 4"

if [ "$RAM_LT128" = "1" ] ; then
  LOWEST_PORT="1024"
  HIGHEST_PORT="4999"
else
  # The default setting of local port range for machines with RAM>128Mb
  # is 32768:61000, see /proc/sys/net/ipv4/ip_local_port_range.
  # Here I narrow this range to avoid confusion with a couple of ports
  # used by Trojans:
  LOWEST_PORT="33310"
  # I restored the HIGHEST_PORT since there are (minor) problems with dnsmasq:
  # it doesn't respect the setting and thus some requests get blocked
  HIGHEST_PORT="65535"
#  HIGHEST_PORT="61000"
fi

SUP_PORTS="$LOWEST_PORT:$HIGHEST_PORT"

# The following variables apply to other client machines but not this
# one.

if [ $FIRST_CLIENT_PORT ] ; then
  if [ "$FIRST_CLIENT_PORT" = "1" ] ; then
    IGNORE_CLIENT_PORTS=1
  else 
    CLIENT_PORTS="$FIRST_CLIENT_PORT:"
    LAST_PRIV_PORT=`echo $[ $FIRST_CLIENT_PORT - 1 ]`
    PRIV_PORTS="1:$LAST_PRIV_PORT"
    unset LAST_PRIV_PORT
  fi
else
  CLIENT_PORTS="1024:"
  PRIV_PORTS="1:1023"
fi

#
# Here the game begins. :-)
#

# A list of network devices to protect
IFACES="$EXT_IF $INT_IF"

# Check whether the external interface is already up:
EXT_IP=`/sbin/ifconfig $EXT_IF 2> /dev/null | grep inet | cut -d : -f2 | \
        cut -d \  -f1`

if [ $INT_IF ] ; then
  LAN_IF="$INT_IF"
elif [ $TRUSTED_IF ] ; then
  LAN_IF="$TRUSTED_IF"
fi

if [ $LAN_IF ] ; then
  INT_IP=`/sbin/ifconfig $LAN_IF 2> /dev/null | grep inet | cut -d : -f2 | \
        cut -d \  -f1`
  if [ $INT_IP ] ; then
    INT_MASK=`/sbin/ifconfig $LAN_IF | grep Mask | cut -d : -f4`
    INT_NET="$INT_IP/$INT_MASK"
    unset INT_MASK
  fi
fi

###############################################################################
. $SHAREDIR/tech.functions
. $SHAREDIR/spec.functions
###############################################################################
#
# 1. Flush and delete all chains and set default policies
#

iptables -F
iptables -X
iptables -Z
iptables -t mangle -F
iptables -t mangle -X
if [ "$NAT" = "1" ] ; then
  iptables -t nat -F
  iptables -t nat -X
fi

iptables -P INPUT   DROP
iptables -P OUTPUT  DROP
iptables -P FORWARD DROP

# There is no need to set ACCEPT policy for mangle and nat
# tables since this is the default one.

# This is not necessary but doesn't hurt
echo 0 > /proc/sys/net/ipv4/ip_forward

#
# 2. Load necessary modules
#

/sbin/modprobe ip_conntrack_ftp
if [ "$NEED_IRC" = "1" ] ; then
  /sbin/modprobe ip_conntrack_irc
fi

if [ "$NAT" = "1" ] ; then
  /sbin/modprobe ip_nat_ftp
# /sbin/modprobe ip_nat_snmp_basic

  if [ "$NEED_IRC" = "1" ] ; then
    /sbin/modprobe ip_nat_irc
  fi
fi
    /sbin/modprobe ip_conntrack_netbios_ns
    
###############################################################################
. $SHAREDIR/special.chains
###############################################################################

echoe "${GREEN}$EXT_IF${NORMAL} \c"

if [ $INT_IF ] ; then
  echo -e -n "and ${GREEN}$INT_IF${NORMAL} \c"
fi
echo -e -n "will be protected\n"
if [ $TRUSTED_IF ] ; then
  echo -e -n "${GREEN}$TRUSTED_IF${NORMAL} is trusted\n"
fi
echo

###############################################################################
if [ "$ROUTER" = "1" ] ; then
  . $SHAREDIR/FORWARD.chain
fi
###############################################################################
. $SHAREDIR/INPUT.OUTPUT.chains
###############################################################################
. $SHAREDIR/ipv4-sysctl.settings
###############################################################################

unset FILE TMPFILE PREFIX

START_TIME=`date +'%b %e %H:%M:%S'`
echo "$START_TIME SlackFire $version started" >> /var/log/syslog

echoe "${GREEN}All done! ${YELLOW}SlackFire${GREEN} is up!${NORMAL}\n"

}

#
# End of START SlackFire
#
###############################################################################

###############################################################################
#
# STOP SlackFire (oh, don't! ;-))
#

stop_slackfire()
{

echoe "\n${RED}Stopping ${YELLOW}SlackFire: \c"

STOP_TIME=`date +'%b %e %H:%M:%S'`
echo "$STOP_TIME SlackFire stopped" >> /var/log/syslog

if [ "$NAT" = "1" ] || [ "$ROUTER" = "1" ] ; then

  iptables -P FORWARD ACCEPT
  iptables -F FORWARD
  iptables -Z FORWARD

  echoe "${RED}Done!${NORMAL}\n"
  iptables -nL FORWARD
  echoe "\n${GREEN}Network traffic is forwarded freely!${NORMAL}\n"

else

  #
  # Reset the default policies in the filter table.
  #

  iptables -P INPUT   ACCEPT
  iptables -P OUTPUT  ACCEPT
  iptables -P FORWARD ACCEPT

  iptables -F
  iptables -X
  iptables -Z
  iptables -F -t mangle
  iptables -X -t mangle
  echoe "${RED}Done!${NORMAL}\n"
  iptables -nL

  #
  # Remove a module that is no longer needed:
  #

  /sbin/modprobe -r ip_conntrack_ftp
  if [ "$NEED_IRC" = "1" ] ; then
    /sbin/modprobe -r ip_conntrack_irc
  fi

  echoe "\n${GREEN}Network traffic is flowing freely!${NORMAL}\n"

fi

}

#
###############################################################################

###############################################################################
#
# Finally, this is how the script should be used
#

case "$1" in
  'start'|'up'|'on')
    start_slackfire
  ;;
  'stop'|'down'|'off')
    stop_slackfire
  ;;
  *)
    echo "Usage:"
    echoe "\t`/usr/bin/basename $0` start | on | up"
    echo "and"
    echoe "\t`/usr/bin/basename $0` stop | off | down"
  ;;
esac
exit 0

#
# Voila! :^)
#
###############################################################################
