#!/bin/sh

PWMODULE=esipw
PWMODULE_EXT=esipw.o

[ -z "$AVLIB" ] && AVLIB="."
[ -z "$AVBIN" ] && AVBIN="."
[ -z "$AVDATA" ] && AVDATA="."

# We are run by the scheduler daemon, whose inherited stdout can be a closed or
# broken fd -- our purely-informational status echoes would then fail with
# "echo: I/O error". The real work writes to files, so send our own
# stdout/stderr to a rolling log (or /dev/null) and never let chatter fail us.
if [ -d "$AVDATA" ] && [ -w "$AVDATA" ]; then
    exec >>"$AVDATA/pwcheck.log" 2>&1
else
    exec >/dev/null 2>&1
fi

# force = Character: "b" - Best fit, (k, p)
#                    "p" - pwpcap,
#                    "k" - pwkernel
force="b"

# modules = Strings: The module, or list of modules to try to link, if they exist.
# Pull the list of modules from the command line if there is one, or
#  AVLIB/modules/*/esipw.o if AVLIB is defined, or "./*".
#  Actually using a 'find' here means we will search whole subdir trees
#  looking for the module, probably a bit aggressive but very maintainable.
modules=

# The directory to search for modules
moddir=

# matchMod = String: Contains the path to the winning linkable moduleo,
#                    or "None" if no matching module was found.
matchMod="None"

# os_type = String: "SunOS", "Linux", "CYGWIN_NT-5.1/5.2/6.1", "MINGW32_NT-5.1/5.2/6.1" "AIX", "Darwin"
if [ -x /bin/uname ]; then 
    os_type=`/bin/uname -s`
elif [ -x /usr/bin/uname ]; then
    os_type=`/usr/bin/uname -s`
else
    echo "Cannot find uname in /bin or /usr/bin, exiting."
    exit 1
fi

# pwkernel = String: "pwsolaris", "pwlinux"
pwkernel=

# statefile = String: Filename of the file that contains the state information
#                     for poliwall runtime.  This file shows up in AVDATA.
statefile="pwstate.txt"

#----------------------------------------------
option_info="$*"
while getopts pkm:d: o; do
    case "$o" in
    p)	  force="p";;
    k)	  force="k";;
    d)	  moddir="$OPTARG";;
    [?])  echo "Usage: $0 [-p] [-k] [-d module_dir] [module_file_list]"
	    exit 1;;
    esac
done
shift `expr $OPTIND - 1`
modules="$@"

if [ $os_type = "SunOS" ]; then
    DEVLINK=${BASEDIR}/etc/devlink.tab
    pwkernel="pwsolaris"

    if [ -n "$modules" ]; then
	echo "Solaris uses module directories, not module names directly."
    fi

    if [ -z "$moddir" ]; then
	moddir=${AVLIB}/modules/sol`/bin/uname -r`/
    fi

    if [ `/bin/isainfo -k` = "sparcv9" ]; then
	modules=${PWMODULE}64
    else
	modules=${PWMODULE}32
    fi

    if [ -d ${moddir} -a -r ${moddir}${modules} ]; then
	matchMod=${moddir}
	if [ ! -r ${moddir}${PWMODULE}.conf ]; then
	    (cd ${moddir}; echo "name=\"${PWMODULE}\" parent=\"pseudo\" instance=0;" > ${PWMODULE}.conf)
	fi
    fi

    if [ x"`/bin/grep minor=${PWMODULE} ${DEVLINK}`" = x ] ; then
	echo "type=ddi_pseudo;name=${PWMODULE};minor=${PWMODULE}	\M0" >> ${DEVLINK}
    fi

    if [ ! -c /dev/pfil ]; then
	matchMod="None"
	echo "pfil package is not installed."
    else
	# For each interface, insert pfil after ip in modlist if it doesn't
	# already exist there.
	#   ifconfig <interface> modlist
	#    position = ip + 1
	#   ifconfig <interface> modinsert pfil@${position}
	# if [ x"`/sbin/ifconfig -au4 modlist 2>/dev/null | /bin/grep pfil`" = x ]
	# then
	    # Insert pfil after ip.
	    # pos=`/sbin/ifconfig -au4 modlist 2>/dev/null | \
		# 	/bin/awk '/ ip$/ { print $1 } ' - 2>/dev/null`
	    # echo "pfil not installed on any interfaces, should be after ${pos}."
	    # echo "Run ifconfig <interface> modinsert pfil@${pos}+1."
	    # matchMod="None"
	# else
	    # echo "pfil insert on at least one interface. "
	# fi
	echo "."
    fi
elif [ $os_type = "HP-UX" ]; then
    pwkernel="pwhpux"

    if [ -n "$modules" ]; then
	echo "HP-UX uses module directories, not module names directly."
    fi

    modules=${PWMODULE}64

    if [ -z "$moddir" ]; then
	moddir=${AVLIB}/modules/hpux`/bin/uname -r`/
    fi


    if [ -d ${moddir} -a -r ${moddir}${modules} ]; then
	matchMod=${moddir}
    fi

#    if [ ! -c /dev/pfil ]; then
#	matchMod="None"
#	echo "pfil package is not installed."
#    fi
elif [ $os_type = "AIX" ]; then
    # AIX version is single digit: AIX 5L 5.3 gives "3" for uname -r.
    # Module subdirectory name for that case is aix3.
    pwkernel="pwAIX"

    if [ -z "$moddir" -a -z "$modules" ]; then
	moddir=${AVLIB}/modules/aix`uname -r`/
    fi

    # If no module paths from command line, look for possibles.
    if [ -z "$modules" ]; then
	modules=${PWMODULE}.o
    fi

    # No trial loading in AIX, just take fixed path in modules.
    if [ -d ${moddir} -a -r ${moddir}${PWMODULE}.o ]; then
	matchMod=${moddir}${PWMODULE}.o
    else
	matchMod="None"
    fi

    if [ $force = "k" -a $matchMod = "None" ]; then
	echo "No matching kernel module. Linking pcap instead."
    fi
elif [ $os_type = "Darwin" ]; then
    pwkernel=pwdarwin
    PWMODULE_EXT=${PWMODULE}.kext

    if [ -z "$moddir" -a -z "$modules" ]; then
	tmpdir=${AVLIB}/modules/darwin`/usr/bin/uname -r|awk -F . "{print \\$1}"`
	moddir=`echo $tmpdir*`/
    fi

    # If no module paths from command line, look for possibles.
    if [ -z "$modules" ]; then
	modules=${PWMODULE_EXT}
    fi

    # No trial loading in Darwin, just take fixed path in modules.
    if [ -d ${moddir} -a -r ${moddir}${PWMODULE_EXT} ]; then
	matchMod=${moddir}${PWMODULE_EXT}
    else
	matchMod="None"
    fi

    if [ $force = "k" -a $matchMod = "None" ]; then
	echo "No matching kernel module. Linking pcap instead."
    fi
else
    # Assume linux, eh?
    pwkernel="pwlinux"

    # Prefer the NFQUEUE userspace backend (verdict-capable, no kernel module).
    # The legacy kernel module below is retained only as a fallback and is no
    # longer built or shipped by default.
    if [ -x ${AVBIN}/pwnfqueue ] && ! ldd ${AVBIN}/pwnfqueue 2>/dev/null | grep -q "not found"; then
	echo "Using NFQUEUE userspace backend (verdict-capable)"
	(cd ${AVBIN}; /bin/rm -f pwfilter; ln -s pwnfqueue pwfilter)
	(cd ${AVDATA}; /bin/rm -f $statefile; echo "options=$option_info" > $statefile)
	(cd ${AVDATA}; echo "kernel=False" >> $statefile)
	(cd ${AVDATA}; echo "backend=nfqueue" >> $statefile)
	exit 0
    fi
    echo "NFQUEUE backend unavailable; falling back to legacy kernel module."

    if [ -z "$moddir" -a -z "$modules" ]; then
	moddir=${AVLIB}/modules/
    fi

    if [[ "`uname -r`" > "2.6." ]]; then
	EXTENSION="ko"
	# 2.6 kernels don't allow probing before insertion.  So, if you already
	# have a kernel module installed, remove it first.
	#  If it is in use, this will fail the current run of pw_check will
	#  default to non-enforcing.
	res=`/sbin/rmmod ${PWMODULE} > /dev/null 2>&1`
    else
	EXTENSION="o"
    fi

    if [ -z "$modules" ]; then
	for mp in `/bin/ls ${moddir}`; do
	    if [ -d ${moddir}${mp} -a -e ${moddir}${mp}/${PWMODULE}.${EXTENSION} ]; then
		modules="${modules} ${moddir}${mp}/${PWMODULE}.${EXTENSION}"
	    fi
	done
    fi

    for matchMod in ${modules}; do
	if [ "${EXTENSION}" == "ko" ]; then
	    res=`/sbin/insmod $matchMod > /dev/null 2>&1`
	    if [ $? == 0 ]; then
		res=`/sbin/rmmod ${PWMODULE} > /dev/null 2>&1`
		break
	    fi
	else
	    res=`/sbin/insmod -pq -o p_$matchMod $matchMod > /dev/null 2>&1`
	    if [ $? == 0 ]; then
		break
	    fi
	fi
	matchMod="None"
    done

    if [ $force = "k" -a $matchMod = "None" ]; then
	echo "No matching kernel module. Linking pcap instead."
    fi
fi

(cd ${AVDATA}; /bin/rm -f $statefile; echo "options=$option_info" > $statefile)
(cd ${AVDATA}; echo "module=$matchMod" >> $statefile)

if [ $matchMod = "None" -o $force = "p" ]; then
    echo "No Match"

    # Try userspace verdict-capable backends before falling back to pwpcap.
    # These can enforce (drop/accept) without a kernel module.
    if [ $os_type = "Linux" -a -x ${AVBIN}/pwnfqueue ] && ! ldd ${AVBIN}/pwnfqueue 2>/dev/null | grep -q "not found"; then
	echo "Using NFQUEUE userspace backend (verdict-capable)"
	(cd ${AVBIN}; /bin/rm -f pwfilter; ln -s pwnfqueue pwfilter)
	(cd ${AVDATA}; echo "kernel=False" >> $statefile)
	(cd ${AVDATA}; echo "backend=nfqueue" >> $statefile)
	exit 0
    elif [ $os_type = "Darwin" -a -x ${AVBIN}/pwnwext ]; then
	echo "Using Network Extension userspace backend (verdict-capable)"
	(cd ${AVBIN}; /bin/rm -f pwfilter; ln -s pwnwext pwfilter)
	(cd ${AVDATA}; echo "kernel=False" >> $statefile)
	(cd ${AVDATA}; echo "backend=nwext" >> $statefile)
	exit 0
    fi

    # No verdict-capable userspace backend available; fall back to pwpcap
    # (monitoring only, cannot drop packets)
    echo "Falling back to pwpcap (monitoring only)"
    (cd ${AVBIN}; /bin/rm -f pwfilter; ln -s pwpcap pwfilter)
    # Remove linkage to any module that used to link.
    (cd ${AVLIB}; /bin/rm -f ${PWMODULE_EXT})
    (cd ${AVDATA}; echo "kernel=False" >> $statefile)
    (cd ${AVDATA}; echo "backend=pcap" >> $statefile)
    exit 1
else
    echo "Match for $matchMod"
    # Link pwfilter to pwkernel which will use kernel mode poliwall
    if [ -f $pwkernel64 ]; then
	(cd ${AVBIN}; /bin/rm -f pwfilter; /bin/ln -s $pwkernel pwfilter)
    else
	(cd ${AVBIN}; /bin/rm -f pwfilter; /bin/ln -s $pwkernel pwfilter)
    fi
    # Link ${PWMODULE_EXT} to the module that should be loaded
    # On Solaris this links the name to the directory where the modules exist.
    # Likewise Darwin: directory is a "kernel extension bundle."
    (cd ${AVLIB}; /bin/rm -f ${PWMODULE_EXT}; /bin/ln -s $matchMod ${PWMODULE_EXT})
    (cd ${AVDATA}; echo "kernel=True" >> $statefile)
    (cd ${AVDATA}; echo "backend=kernel" >> $statefile)
    exit 0
fi
