#! @SH@
#
# $NetBSD: audit-packages,v 1.26 2005/11/21 10:39:50 agc 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=1
PKG_INSTALL_REQUIRED=20050530

usage() {
	argv0="${1##*/}"
	cat <<EOF
$2
Usage: $argv0 [-dv] [-i vulnid:id|pkgpat:pattern]
		     [-K pkg_dbdir] [-p package]
    -d : Run download-vulnerability-list before anything else.
    -i : Ignore packages matching one of the specified vulnerabilities,
          or matching one of the provided patterns.
          Repeated -i options add to the ignore list.
    -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
ignore_list=
pkg_patterns=
vulnids=
one_package=
while [ $# -gt 0 ]; do
	case "$1" in
	-d)	download=yes ;;
	-v)	verbose=yes ;;
	-i)
		ignore_list="$ignore_list $2"
		shift
		;;
	-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

for ign in $ignore_list ; do
	case "$ign" in
	pkgpat:*)
		ign="${ign#*:}"
		pkg_patterns="$pkg_patterns $ign"
		;;
	*)
		vulnids="$vulnids $ign"
		;;
	esac
done

# 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

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

	vulnid=unknown
	if [ $file_teeny -gt 0 ] ; then
		vulnid=${type%%,*}
		type=${type#*,}

		skipit=0
		for ign in $vulnids; do
			if [ "$ign" = "$vulnid" ] ; then
				skipit=1
			fi
		done
		if [ $skipit -eq 1 ] ; then
			continue;
		fi
	fi

	if [ -z "$one_package" ] ; then
		vulnpkgs=`@PKG_TOOLS_BIN@/pkg_info -e "$pat"`
	else
		vulnpkgs=
		if `@PKG_TOOLS_BIN@/pkg_admin pmatch "$pat" "$one_package"` ; then
			vulnpkgs=$one_package
		fi
	fi
	for pkg in $vulnpkgs ; do
		skipit=0
		for ign in $pkg_patterns; do
			ign="${ign#*:}"
			if @PKG_TOOLS_BIN@/pkg_admin pmatch "${ign}" "$pkg" ; then
				skipit=1
				break
			fi
		done
		if [ $skipit -eq 1 ] ; then
			continue
		fi
		echo "Package $pkg has a" \
			"$type vulnerability (vulnid:$vulnid), see $url"
	done
done < "$vuls"

exit 0
