#!/bin/sh
#
#   This is the gabriel report generation program.  Its purpose is
#   to look at data sent from the various gabriel clients and act
#   on the data according to a configuration file.
#
# Author: Richard Mahn
#
#
#Copyright 1995 by Los Altos Technologies Inc.
#All rights reserved.
#
#Redistribution and use in source and binary forms are
#permitted provided that this entire copyright notice is
#duplicated in all such copies.  No charge, other than "at-cost"
#distribution fee may be charged for copies, derivations, or
#distributions of this material without the expressed written consent
#of the copyright holders.
#
#THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT ANY EXPRESSED
#OR IMPLIED WARRANTIES, INCLUDING, WITHOUT FURTHER LIMITATION, THE
#IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ANY
#PARTICULAR PURPOSE.
#
#IN NO EVENT SHALL LOS ALTOS TECHNOLOGIES, INC., OR ANY OF ITS
#EMPLOYEES, REPRESENTATIVE, AGENTS, OFFICERS, OR DIRECTORS, OR ANY
#CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING BUT NOT
#LIMITED TO, LOSS OF USE, DATA, PROFITS, OR BUSINESS INTERRUPTION)
#HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
#CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
#OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
#EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.



#
#  this script file contains the subroutines first, followed by
#  the main program, which is relatively short
#

####################
#  initialization  #
####################

initialize () {
    LOG_FILE="/var/adm/gabriel.log"
    CONFIG_FILE="/etc/gabriel.conf"

    LOGGER="/usr/ucb/logger"
    PRIORITY="local3"
    IDENTIFIER="gabriel"

    USAGE="$PROGRAM: [-v] [-i] [-c config_file] [-f inputfile]"
    LONG_USAGE="$USAGE
    where
	-h[elp]	this message
	-v	verbose
	-i	interactive--just send info to the tty
	-a	all--use all data in the inputfile instead of just the
		recent data
	-c	to specify the configuration file
	-f	to specify the file containing the syslog entries"
    verbose='n'
    interactive='n'
    full_data='n'
}

# full usage message
usage () {
    echo "$LONG_USAGE"
    exit 0
}

# error exit
exit_with_usage () {
    echo $USAGE 2>&1
    exit 1
}

# verify files exist
verify_files () {
    if [ ! -r $LOG_FILE ] ; then
	echo "$PROGRAM: can't read $LOG_FILE" 2>&1;
	exit 1;
    fi
    if [ ! -r $CONFIG_FILE ] ; then
	echo "$PROGRAM: can't read $CONFIG_FILE" 2>&1;
	exit 1;
    fi
}

# get some cut of data
#    #1 == 1, 2, or 3 for severity
#    #2 == y for full collection
select_data () {
    _SEV=$1
    _FULL=$2
    case $_SEV in
		# set _F for field number, _MN and _MX for min/max severity
		# to collect on
	1)
	    _F=4;	# collector
	    _MN=1;	# min severity
	    _MX=3;	# max severity
	    ;;
	2|3)
	    _F=7;	# attacker
	    _MN=$_SEV;	# min severity
	    _MX=$_SEV;	# max severity
    esac

awk	'
    BEGIN	{ n=0; }
    $5 != id":"	{ next; }	# only our messages
		{ sev=$6; }
    sev == 0	{	# clear the data and start over
		  if ( full != "y" ) {
		    if (n > 0)
			for (x in list) list[x] = "";
		    n=0;
		  }
		}
    sev >= mn && sev <= mx { list[$i] = $i; n++; }
    END		{
		if (n > 0) {
		    for (x in list)
			if (list[x] != "") printf "\t%s\n",x;
		}
		}
	' id=$IDENTIFIER full=$_FULL i=$_F mn=$_MN mx=$_MX $LOG_FILE 

}


# routine to perform an action
do_function () {
    _TYPE=$1;
    _FUNC=$2;
    _ARGS=$3;

    case $_TYPE in
	info)
	    test "$REPORTERS" || return;
	    set -- $REPORTERS
	    if [ $# -eq 1 ] ; then
		_MAIL_MESSAGE="$NOW $IDENTIFIER has received status form 1 host:"
	    else
		_MAIL_MESSAGE="$NOW $IDENTIFIER has received status from $# hosts:"
	    fi
	    while [ $# -ge 1 ] ; do
		_MAIL_MESSAGE="$_MAIL_MESSAGE
	$1"
		shift;
	    done
	    if [ "$REPORTERS" ] ; then
		_LOG_MESSAGE="$NOW recent status from $REPORTERS"
	    else
		_LOG_MESSAGE="$NOW no recent status data"
	    fi
	    ;;
	warning)
	    test "$LITTLES" || return;
	    set -- $LITTLES
	    if [ $# -gt 1 ] ; then
		_MAIL_MESSAGE="$NOW $IDENTIFIER has detected restrained attacks from these hosts:"
	    else
		_MAIL_MESSAGE="$NOW $IDENTIFIER has detected restrained attacks from this host:"
	    fi
	    while [ $# -ge 1 ] ; do
		_MAIL_MESSAGE="$_MAIL_MESSAGE
	$1"
		shift
	    done
	    _LOG_MESSAGE="$NOW restrained attacks from $LITTLES"
	    ;;
	alert)
	    test "$BIGS" || return;
	    set -- $BIGS
	    if [ $# -gt 1 ] ; then
		_MAIL_MESSAGE="$NOW $IDENTIFIER has detected unbridled attacks from these hosts:"
	    else
		_MAIL_MESSAGE="$NOW $IDENTIFIER has detected unbridled attacks from this host:"
	    fi
	    while [ $# -ge 1 ] ; do
		_MAIL_MESSAGE="$_MAIL_MESSAGE
	$1"
		shift
	    done
	    _LOG_MESSAGE="$NOW restrained attacks from $BIGS"
	    ;;
	*)	# this shouldn't happen
	    echo "$PROGRAM: error occurred processing type $_TYPE" 2>&1
	    ;;
    esac

    _WRITE_MESSAGE="$_MAIL_MESSAGE"

    case $_FUNC in
	mail)
	    echo "$_MAIL_MESSAGE" | eval $_ARGS
	    ;;
	call)
	    eval $_ARGS
	    ;;
	write)
	    echo "$_WRITE_MESSAGE" | write $_ARGS >/dev/null 2>&1
	    ;;
	log)
	    set -- $_ARGS
	    _FILE=$1;
	    touch $_FILE &&
		echo $_LOG_MESSAGE >> $_FILE ||
		echo "$PROGRAM: error writing logfile $_FILE" 2>&1
	    ;;
	*)	# this shouldn't happen
	    echo "$PROGRAM: error occurred processing function $_FUNC" 2>&1
	    ;;
    esac
}

# routine to send a syslog message
send_syslog_msg () {
    _MESSAGE=$1;
    $LOGGER -p ${PRIORITY}.info -t $IDENTIFIER "$_MESSAGE"
}

##############################
#  main program begins here  #
##############################

PROGRAM=$0
PATH="/bin:/usr/bin"
initialize

# parse arguments
while [ $# -ge 1 ] ; do
    case $1 in
	-v) verbose='y' ;;
	-i) interactive='y' ;;
	-h|-help) usage ;;
	-a) full_data='y' ;;
	-f)
	    if [ $# -lt 2 ] ; then
		exit_with_usage
	    fi
	    shift;
	    LOG_FILE=$1;
	    ;;
	-c)
	    if [ $# -lt 2 ] ; then
		exit_with_usage
	    fi
	    shift;
	    CONFIG_FILE=$1;
	    ;;
	*)
	    exit_with_usage
	    ;;
    esac
    shift;
done

verify_files

NOW=`date '+%a %D %T'`

REPORTERS=`select_data 1 $full_data`
LITTLES=`select_data 2 $full_data`
BIGS=`select_data 3 $full_data`

#
# if we are interactive, then we don't
# send any e-mail, call pagers, or send syslog messages
#
if [ "$interactive" = 'y' ] ; then
    date +"$IDENTIFIER activity summary as of %T on %a, %h %d, 19%y"
    echo
    set -- $REPORTERS
    case $# in
	0)  echo $IDENTIFIER has not received data from any hosts ;;
	1)  echo $IDENTIFIER has received data from this host: ;;
	*)  echo $IDENTIFIER has received data from these $# hosts: ;;
    esac
    while [ $# -ge 1 ] ; do
	echo "	$1"
	shift
    done
    if [ "$BIGS" = "" -a "$LITTLES" = "" ] ; then
	echo $IDENTIFIER has not detected any attacks
    else
	set -- $LITTLES
	case $# in
	    0)	echo $IDENTIFIER has not detected any restrained attacks ;;
	    1)	echo $IDENTIFIER has detected restrained attacks from this host: ;;
	    *)	echo $IDENTIFIER has detected restrained attacks from these $# hosts: ;;
	esac
	while [ $# -ge 1 ] ; do
	    echo "	$1"
	    shift
	done

	set -- $BIGS
	case $# in
	    0)	echo $IDENTIFIER has not detected any unbridled attacks ;;
	    1)	echo $IDENTIFIER has detected unbridled attacks from this host: ;;
	    *)	echo $IDENTIFIER has detected unbridled attacks from these $# hosts: ;;
	esac
	while [ $# -ge 1 ] ; do
	    echo "	$1"
	    shift
	done
    fi
    exit
fi

#  parse the configuration file and act on it accordingly
line_no='';
while read level function params ; do
    line_no="${line_no}x"
    case $level in
	'')	;;	# skip blank lines
	'#'*)	;;	# skip comments
	warning|alert|info)
	    case $function in
		mail|call|write|log)
		    do_function $level $function "$params"
		    ;;
		*)
		    echo "$PROGRAM: invalid function ($function) in line `expr length $line_no`:" 2>&1
		    echo ">>>>	$level $function $params <<<<" 2>&1
		    ;;
	    esac
	    ;;
	*)
	    echo "$PROGRAM: line `expr length $line_no` is unrecognizable:" 2>&1
	    echo ">>>>	$level $function $params	<<<<" 2>&1
	    ;;
    esac
done < $CONFIG_FILE

# send syslog message 

set -- $REPORTERS
case $# in
   0) MSG="0 no new data,";;
   1) MSG="0 data from 1 host,";;
   *) MSG="0 data from $# hosts,";;
esac
set -- $LITTLES
case $# in
    0) MSG="$MSG no restrained attacks,";;
    1) MSG="$MSG 1 restrained attack,";;
    *) MSG="$MSG $# restrained attacks,";;
esac
set -- $BIGS
case $# in
    0) MSG="$MSG no unbridled attacks.";;
    1) MSG="$MSG 1 unbridled attack.";;
    *) MSG="$MSG $# unbridled attacks.";;
esac

send_syslog_msg "$MSG"

exit 0;

