#!/bin/sh
# sshguard -- protect hosts from brute-force attacks

libexec="/opt/local/libexec"
version="2.5.1"

err() {
    echo "sshguard: $1" >&2
}

setflag() {
    if [ -n "$2" ]; then
        flags="$flags -$1 $2"
    fi
}

usage() {
    cat << EOF
Usage: sshguard [-v] [-h]
[-a BLACKLIST-THRESHOLD] [-b BLACKLIST-FILE]
[-i PID-FILE] [-p BLOCK_TIME]
[-s DETECTION_TIME] [-w IP-ADDRESS | WHITELIST-FILE]
EOF
}

clean_and_exit() {
    if [ -n "$PID_FILE" ]; then
        rm -f "$PID_FILE"
    fi
    exit
}

# Source configuration file
config="/opt/local/etc/sshguard.conf"
if [ ! -r $config ]; then
    err "Could not read '$config'"
    err "Please configure SSHGuard."
    exit 78
fi

. $config

# Runtime arguments override configuration options
while getopts "b:l:p:s:a:w:i:hv" opt; do
    case $opt in
        a) THRESHOLD=$OPTARG;;
        b) BLACKLIST_FILE=$OPTARG;;
        i) PID_FILE=$OPTARG;;
        l) FILES="$FILES $OPTARG";;
        p) BLOCK_TIME=$OPTARG;;
        s) DETECTION_TIME=$OPTARG;;
        w) WHITELIST_ARG="$WHITELIST_ARG $OPTARG";;
        h) usage; exit;;
        v) echo "SSHGuard $version"; exit;;
        *) echo "Try 'sshguard -h' for help"; exit 1;;
    esac
done

# Check backend
if [ -z "$BACKEND" ]; then
    err "BACKEND must be set in '$config'"
    exit 78
elif [ ! -x "$BACKEND" ]; then
    err "'$BACKEND' is not executable"
    exit 78
fi

# Read config in to flags
setflag 'a' "$THRESHOLD"
setflag 'b' "$BLACKLIST_FILE"
setflag 'p' "$BLOCK_TIME"
setflag 's' "$DETECTION_TIME"
setflag 'N' "$IPV6_SUBNET"
setflag 'n' "$IPV4_SUBNET"
if [ -n "$WHITELIST_ARG" ]; then
    for arg in $WHITELIST_ARG; do
      flags="$flags -w $arg"
    done
elif [ -n "$WHITELIST_FILE" ]; then
    flags="$flags -w $WHITELIST_FILE"
fi

# Log source selection order:
#     runtime args, logreader and files, logreader, files, or stdin
shift $((OPTIND-1))
if [ $# -gt 0 ]; then
    tailcmd="$libexec/sshg-logtail $@"
elif [ -n "$LOGREADER" -a -n "$FILES" ]; then
    LOGREADER="$LOGREADER | grep --line-buffered '^'"
    FILESREAD="$libexec/sshg-logtail $FILES | grep --line-buffered '^'"
    tailcmd="( $LOGREADER & $FILESREAD )"
elif [ -n "$LOGREADER" ]; then
    tailcmd="$LOGREADER"
elif [ -n "$FILES" ]; then
    tailcmd="$libexec/sshg-logtail $FILES"
elif [ -z "$tailcmd" ]; then
    err "$config is missing FILES and LOGREADER; please specify one"
    exit 1
fi

if [ -n "$PID_FILE" ]; then
    if [ ! -e "$PID_FILE" ]; then
        echo "$$" > $PID_FILE
    else
        err "$PID_FILE already exists; is SSHGuard already running?"
        exit 1
    fi
fi

# Select PARSER from configuration file or use default. Add POST_PARSER from
# configuration file if it exists.
RUN_PARSER=${PARSER:-$libexec/sshg-parser}
if [ -n "$POST_PARSER" ]; then
    RUN_PARSER="$RUN_PARSER | $POST_PARSER"
fi

# Set environment
if [ -n "$SSHGUARD_USER" ]; then
    export SSHGUARD_USER="$SSHGUARD_USER"
fi

if [ -n "$STATS_DIR" ]; then
    export SSHGUARD_STATS_DIR="$STATS_DIR"
fi

# Make sure to kill entire process group (subshell) on exit/interrupts.
trap "clean_and_exit" INT TERM
trap "kill 0" EXIT

eval $tailcmd | eval "$RUN_PARSER" | \
    $libexec/sshg-blocker $flags | $BACKEND &
wait
