#! @SH@
#
# $NetBSD: audit-packages,v 1.30 2006/12/09 12:30:47 adrianp Exp $
#
# Copyright (c) 2000-2003 Alistair Crooks.  All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
#	This product includes software developed by Alistair Crooks
#	for the NetBSD project.
# 4. The name of the author may not be used to endorse or promote
#    products derived from this software without specific prior written
#    permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
# GOODS OR SERVICES; LOSS OF USE, DATA, OR 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.
#

ERR_DOWNLOAD="Please run download-vulnerability-list."
ERR_UPGRADE="Please upgrade security/audit-packages to the newest version."
ERR_PKGINSTALL="Please upgrade pkgtools/pkg_install to the newest version."

: ${PKGVULNDIR=@PKGVULNDIR@}

FORMAT_MAJOR=1
FORMAT_MINOR=0
FORMAT_TEENY=0
PKG_INSTALL_REQUIRED=20050530

usage() {
	argv0="${1##*/}"
	cat <<EOF
$2
Usage: $argv0 [-dv] [-K pkg_dbdir] [-p package]
    -d : Run download-vulnerability-list before anything else.
    -K : Use pkg_dbdir as PKG_DBDIR.
    -p : Check a specific package for vulnerabilities.
    -v : Verbose mode
EOF
	exit 1
}

settingsmsg=""
if [ -r @PKG_SYSCONFDIR@/audit-packages.conf ]; then
	settingsmsg="Reading settings from @PKG_SYSCONFDIR@/audit-packages.conf"
	. @PKG_SYSCONFDIR@/audit-packages.conf
fi

vuls="${PKGVULNDIR}/pkg-vulnerabilities"

download=no
verbose=no
one_package=
while [ $# -gt 0 ]; do
	case "$1" in
	-d)	download=yes ;;
	-v)	verbose=yes ;;
	-p)
		one_package="$2"
		shift
		;;
	-K)
	 	export PKG_DBDIR="$2"
		shift
		;;
	*)
		usage "$0" "Unknown option $1"
	esac
	shift
done

case $verbose in
yes)	case "$settingsmsg" in
	"")	;;
	*)	echo "$settingsmsg"
		;;
	esac
	;;
esac

# try to download vulnerability list, as requested
# the integrity of the list is checked below
# so just issue a warning if there was a failure
case "$download" in
yes)	@PREFIX@/sbin/download-vulnerability-list || \
		echo "***WARNING***: download-vulnerability-list failure" 1>&2;;
esac

errmsg=""

# check for missing vulnerabilities file
if [ ! -f "$vuls" ]; then
	errmsg="Missing vulnerabilities file $vuls"
	errsolution="$ERR_DOWNLOAD"
fi

case "$errmsg" in
"")	# check for old vulnerabilities file if we're being verbose
	case "$verbose" in
	# XXX: quote vuls
	yes)	[ -n "$(@FIND@ $vuls -ctime +7)" ] && echo "*** WARNING - $vuls more than a week old, continuing..." ;;
	esac
	;;
esac

case "$errmsg" in
"")	# check that pkg_info is new enough (supports ranges)
	if [ `@PKG_TOOLS_BIN@/pkg_info -V` -lt "$PKG_INSTALL_REQUIRED" ]; then
		errmsg='Installed pkg_info is too old.'
		errsolution="$ERR_PKGINSTALL"
	fi
	;;
esac

case "$errmsg" in
"")	# check format version of vulnerabilities file
	file_major=`@AWK@ '$1 == "#FORMAT" { split($2, a, "\\\\."); print a[1]; exit; }' "$vuls"`
	file_minor=`@AWK@ '$1 == "#FORMAT" { split($2, a, "\\\\."); print a[2]; exit; }' "$vuls"`
	file_teeny=`@AWK@ '$1 == "#FORMAT" { split($2, a, "\\\\."); print a[3]; exit; }' "$vuls"`
	if [ -z "$file_teeny" ] ; then
		file_teeny=0
	fi
	if [ "x$file_major" = "x" -o "x$file_minor" = "x" ]; then
		errmsg="No file format version found in $vuls"
		errsolution="$ERR_DOWNLOAD"
	elif [ "$file_major" -ne "$FORMAT_MAJOR" -o "$file_minor" -gt "$FORMAT_MINOR" ]; then
		errmsg="Unsupported file format version $file_major.$file_minor${file_teeny:+.${file_teeny}} in $vuls (supported version: $FORMAT_MAJOR.$FORMAT_MINOR.$FORMAT_TEENY)."
		if [ "$file_major" -le "$FORMAT_MAJOR" -o
		     \( "$file_major" -eq "$FORMAT_MAJOR" -a
		        "$file_minor" -lt "$FORMAT_MINOR" \) ]; then
			errsolution="$ERR_DOWNLOAD"
		else
			errsolution="$ERR_UPGRADE"
		fi
	fi
	;;
esac

case "$errmsg" in
"")	# check integrity of vulnerabilities file
	recordedsum=`@AWK@ '$1 == "#CHECKSUM" { print $3 }' "$vuls"`
	recordedalg=`@AWK@ '$1 == "#CHECKSUM" { print $2 }' "$vuls"`
	case "$recordedsum" in
	"")	errmsg="No checksum found in $vuls"
		errsolution="$ERR_DOWNLOAD"
		;;
	*)	case "$recordedalg" in
		"")	errmsg="No checksum algorithm found in $vuls file"
			errsolution="$ERR_DOWNLOAD"
			;;
		*)	calcsum=`@AWK@ '$1 == "#CHECKSUM" || /\$NetBSD.*/ { next } { print }' "$vuls" | @DIGEST@ "$recordedalg"`
			if [ "$recordedsum" != "$calcsum" ]; then
				errmsg="Checksum mismatch - recorded $recordedalg checksum \"$recordedsum\", calculated checksum \"$calcsum\""
				errsolution="$ERR_DOWNLOAD"
			fi
			;;
		esac
		;;
	esac
	;;
esac

# if we have found an error, then complain and exit
case "$errmsg" in
"")	;;
*)	echo "***ERROR*** $errmsg" 1>&2
	echo "** $errsolution" 1>&2
	exit 1
	;;
esac

found_vulnpkg=0
# check for vulnerabilities
while read pat type url; do
	case "$pat" in
	\#*|'') continue;;
	esac

	case "$IGNORE_URLS" in
	"")	;;
	*)	ignore=false;
		for u in $IGNORE_URLS; do
			if [ "x$u" = "x$url" ]; then
				ignore=true
				break
			fi
		done
		if $ignore; then
			case "$verbose" in
			yes)	echo "Ignoring vulnerability for $url with pattern $pat"
				;;
			esac
			continue
		fi
		;;
	esac

	if [ -z "$one_package" ] ; then
		vulnpkgs=`@PKG_TOOLS_BIN@/pkg_info -e "$pat"`
	else
		vulnpkgs=
		one_pkg=`@PKG_TOOLS_BIN@/pkg_info -e "$one_package"`
		if `@PKG_TOOLS_BIN@/pkg_admin pmatch "$pat" "$one_pkg"` ; then
			vulnpkgs=$one_pkg
		fi
	fi
	for pkg in $vulnpkgs ; do
		found_vulnpkg=`expr $found_vulnpkg + 1`
		echo "Package $pkg has a" \
			"$type vulnerability, see $url"
	done
done < "$vuls"

if [ "$verbose" = "yes" -a "$found_vulnpkg" -eq 0 ]; then
	echo "No vulnerable packages found."
fi

exit 0
