#!/bin/bash
### BEGIN INIT INFO
# Provides:          fiaif
# Required-Start:    $remote_fs
# Required-Stop:     $remote_fs
# Default-Start:     S
# Default-Stop:      0 6
# Short-Description: Intelligent firewall
# Description:       Automates a packet filtering firewall with iptables.
### END INIT INFO
#
# chkconfig: 345 08 92
# description: Automates a packet filtering firewall with iptables.

# FIAIF is an Intelligent firewall$
# Startup script to add firewall functionality.
#
# Script Author:	Anders Fugmann <afu at fugmann dot net>
#
# FIAIF is an Intelligent firewall
# Copyright (C) 2002-2013 Anders Peter Fugmann
# This package comes with ABSOLUTELY NO WARRANTY
# Use strictly at your own risk.
#
# 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 more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

###############################################################################
# load functions
###############################################################################

shopt -s extglob
source /usr/share/fiaif/constants.sh

if [ -r ${CONF_FILE} ]; then
    # Test configuration file
    TMP_FILE=$(mktemp /tmp/fiaif-tmp.XXXXXX)
    awk -f ${FIAIF_SHARED}/syntax.awk \
	-f ${FIAIF_SHARED}/fiaif_rules.awk < ${CONF_FILE} > ${TMP_FILE}
    if (( $? != 0 )); then
	echo "Syntax errors in FIAIF configuration files detected."
	cat ${TMP_FILE}
	echo "Aborting"
	rm -f ${TMP_FILE}
	exit 6
    else
	rm -f ${TMP_FILE}
	source ${CONF_FILE}
	unset TMP_FILE
    fi
fi

source ${FIAIF_SHARED}/iptables.sh
source ${FIAIF_SHARED}/functions.sh
source ${FIAIF_SHARED}/zones.sh
source ${FIAIF_SHARED}/proc-check.sh
source ${FIAIF_SHARED}/sanity_check.sh
source ${FIAIF_SHARED}/cleanup_rules.sh
source ${FIAIF_SHARED}/aliases.sh

function fiaif_start ()
{
    local FIAIF_SAVE_STATE=$1
    local SAVE_STATES=$2
    let ${SAVE_STATES:=1}

    if (( SAVE_STATES == 1 )); then
	save_rules ${NETFILTER_STATE_FILE}
    fi

    load_modules
    local PRE_SCRIPT_LENGTH=${#PRE_SCRIPT[*]}
    if (( PRE_SCRIPT_LENGTH > 0 )); then
	echo "PRE_SCRIPT variable is depricated, and should be replaced"
	echo "by PRE_START_SCRIPT."
	apply_script PRE_SCRIPT PRE_SCRIPT_LENGTH
    fi
    apply_script PRE_START_SCRIPT ${#PRE_START_SCRIPT[*]}

    # Use the state file is available.
    if (( FIAIF_SAVE_STATE == 1 )) && state_valid; then
	restore_rules ${FIAIF_STATE_FILE}

    else
	debug_out "Removing all existing rules, and setting default policies"
	iptables_stop ${DEBUG}
	iptables_setup

	# Test if rules should be saved
	if (( ZONE_ERRORS ==  0 && DEV_ERRORS == 0 && \
	      IPTABLES_ERRORS == 0 && RULE_ERRORS == 0)); then
	    if (( FIAIF_SAVE_STATE == 1 && TEST == 0 )); then
		save_rules ${FIAIF_STATE_FILE}
	    fi
	else
	    print_err "*** FIAIF encountered errors ***"
	    print_err "${DEV_ERRORS} error(s) when testing zone configurations."
	    print_err "${ZONE_ERRORS} reference(s) to undefined zones."
	    print_err "${RULE_ERRORS} error(s) in rule specifications."
	    print_err "${IPTABLES_ERRORS} iptables rule generation error(s)."
	    if (( TEST == 0 )); then
		print_err "Please issue '$0 test' and inspect /tmp/fiaif.out for descriptions."
	    fi
	fi
    fi
    if (( TEST == 0 )); then
	if (( SAVE_STATES == 1 )); then
	    set_proc ${PROC_STATE_FILE}
	else
	    set_proc
	fi
    fi

    local POST_SCRIPT_LENGTH=${#POST_SCRIPT[*]}
    if (( POST_SCRIPT_LENGTH > 0 )); then
	print_err "POST_SCRIPT variables is depricated, and should be replaced"
	print_err "by POST_START_SCRIPT."
	apply_script POST_SCRIPT POST_SCRIPT_LENGTH
    fi
    apply_script POST_START_SCRIPT ${#POST_START_SCRIPT[*]}

    if (( TEST == 0 ));then
	if [[ -z "${NO_CLEANUP}" ]];then
	    # Cleanup - remove unused chains
	    echo -n "Cleaning up rules: "
	    cleanup_rules
	    echo "Done."
	fi

	logger -p syslog.notice -t fiaif "FIAIF started"
	if (( DEBUG == 1 )); then
	    logger -p syslog.crit -t fiaif "DEBUG=1 in fiaif.conf."
	    logger -p syslog.crit -t fiaif \
                "This means that your firewall is wide open"
	fi
    fi
}

function fiaif_stop ()
{
	apply_script PRE_STOP_SCRIPT ${#PRE_STOP_SCRIPT[*]}
	iptables_stop 1
	unload_modules

	#Restore previous state.
	restore_proc ${PROC_STATE_FILE}
	restore_rules ${NETFILTER_STATE_FILE}

	apply_script POST_STOP_SCRIPT ${#POST_STOP_SCRIPT[*]}
	logger -p syslog.notice -t fiaif "FIAIF stopped"
}

function main ()
{
    case "$1" in
	start)
	    if [[ -f ${SUBSYS_FILE} ]]; then
		echo "FIAIF already started. Please stop FIAIF before starting."
		return 1
	    fi
	    fiaif_start ${SAVE_STATE} 1
	    touch ${SUBSYS_FILE}
	    ;;

	stop)
	    if [[ ! -f ${SUBSYS_FILE} ]]; then
		echo "FIAIF has not yet been started."
		return 7
	    fi
	    fiaif_stop

	    # Clean up state files.
	    rm -f ${SUBSYS_FILE}
	    rm -f ${IPTABLES_STATE_FILE} ${PROC_STATE_FILE}
	    ;;


	restart)
	    if [[ ! -f ${SUBSYS_FILE} ]]; then
		echo "FIAIF has not yet been started."
		return 7
	    fi
	    fiaif_start ${SAVE_STATE} 0
	    ;;

	force-reload)
	    touch ${FIAIF_STATE_FILE}
	    rm -fr ${FIAIF_STATE_FILE}
	    fiaif_start ${SAVE_STATE} 0
	    ;;


	status)
	    if [[ -f ${SUBSYS_FILE} ]]; then
		echo "FIAIF is running."
		iptables_status
	    else
		echo "FIAIF is stopped."
		return 3
	    fi
	    ;;

	panic)
	    # Stop the firewall. Do not read DEBUG variable.
	    iptables_stop 0
	    rm -f ${SUBSYS_FILE}
	    ;;

	test)
	    TEST=1
	    # Determine which file to write to.
	    if [[ -n "$2" ]]; then
		TEST_FILE=$2
	    elif [[ -n "${TEST_FILE}" ]]; then
		TEST_FILE=${TEST_FILE}
	    else
		TEST_FILE="/tmp/fiaif.out"
	    fi
	    rm -f ${TEST_FILE}

	    # Dont use the state file
	    fiaif_start 0

	    check_network_settings
	    echo "All rules has been written to ${TEST_FILE}"
	    ;;

	*)
	    echo "Usage: $0 {start|stop|restart|force-reload|status|panic}"
	    return 2
    esac

}

# Set the path
PATH=${BIN_PATH}:${PATH}

# Test that the user is indeed root
if (( EUID != 0 )); then
    echo You must be root to run this program
    exit 4
fi

if [[ ! -r ${CONF_FILE} ]]; then
    echo "FIAIF configuration file '${CONF_FILE}' not found."
    echo "Aborting."
    exit 5
fi

# Dont start if the FIAIF has not been configured.
if [[ -n "${DONT_START}" ]] && (( DONT_START == 1 )); then
    echo "Fiaif is not configured."
    echo "Set 'DONT_START=0' in /etc/fiaif/fiaif.conf"
    exit 6
fi

# Test if iptables program is available.
which iptables > /dev/null
if (( $? != 0 )); then
    echo "Could not find 'iptables'. Aborting."
    exit 5
fi

if [[ -n "${MODULES}" ]]; then
    which modprobe > /dev/null
    if (( $? != 0 )); then
	echo "Could not find 'modprobe'. Aborting."
	exit 1
    fi
fi

# Remove old state file if older than boot time
if [[ -f ${SUBSYS_FILE} ]]; then
    BOOT_TIME=$(grep btime /proc/stat|cut -f 2 -d" ")
    SUBSYS_TIME=$(date +%s -r ${SUBSYS_FILE})
    if (( SUBSYS_TIME < BOOT_TIME )); then
	rm -f ${SUBSYS_FILE}
    fi
fi

let ${SAVE_STATE:=0}
let ${ZONE_ERRORS:=0}
let ${RULE_ERRORS:=0}
let ${DEV_ERRORS:=0}
let ${IPTABLES_ERRORS:=0}
let ${DEBUG:=1}
let ${TEST:=0}

# Global commands.
print_version

if (( DEBUG == 1 )); then
    print_err "*** Warning: DEBUG=1 in fiaif.conf."
    print_err "*** This means that NO packets will ever be dropped,"
    print_err "*** and your firewall will accept all connections."
fi

main $1
