#!/bin/sh -efu # # Copyright (c) 2004 Oliver Eikemeier. 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. Neither the name of the author nor the names of its contributors may be # used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED "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 # COPYRIGHT OWNER OR CONTRIBUTORS 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. # # $FreeBSD$ # portaudit_confs() { portaudit_dir=${portaudit_dir:-"%%DATABASEDIR%%"} portaudit_filename=${portaudit_filename:-"auditfile.tbz"} portaudit_fetch_env=${portaudit_fetch_env:-} portaudit_fetch_cmd=${portaudit_fetch_cmd:-"fetch -1amp"} portaudit_sites=${portaudit_sites:-"http://www.FreeBSD.org/ports/"} if [ -r %%PREFIX%%/etc/portaudit.conf ]; then . %%PREFIX%%/etc/portaudit.conf fi } extract_auditfile() { %%BZIP2_CMD%% -dc -- "$portaudit_dir/$portaudit_filename" | \ tar -xOf - auditfile } checksum_auditfile() { chksum1=`extract_auditfile | sed -nE -e '$s/^#CHECKSUM: *MD5 *([0-9a-f]{32})$/\1/p'` chksum2=`extract_auditfile | sed -e '$d' | md5` [ "$chksum1" = "$chksum2" ]; } getcreated_auditfile() { extract_auditfile | sed -nE -e '1s/^#CREATED: *([0-9]{4})-?([0-9]{2})-?([0-9]{2}) *([0-9]{2}):?([0-9]{2}):?([0-9]{2}).*$/\1-\2-\3 \4:\5:\6/p' } gettimestamp_auditfile() { extract_auditfile | sed -nE -e '1s/^#CREATED: *([0-9]{4})-?([0-9]{2})-?([0-9]{2}).*$/\1\2\3/p' } checkexpiry_auditfile() { created=`gettimestamp_auditfile` expiry=`date -u -v-$1d '+%Y%m%d'` [ "$created" -gt "$expiry" ]; } portaudit_prerequisites() { if $prerequisites_checked; then return 0 fi if [ -z "${pkg_info:-}" ]; then if [ -x "%%LOCALBASE%%/sbin/pkg_info" ]; then pkg_info="%%LOCALBASE%%/sbin/pkg_info" else pkg_info="/usr/sbin/pkg_info" fi fi if [ -z "${pkg_version:-}"]; then case "$pkg_info" in */*) pkg_version="${pkg_info%/*}/pkg_version";; *) pkg_version="pkg_version";; esac fi PKG_INSTALL_VER=`$pkg_info -qP 2>/dev/null` if [ -z "$PKG_INSTALL_VER" -o "$PKG_INSTALL_VER" -lt %%REQPKGVER%% ]; then echo "$pkg_info is too old, please update port sysutils/pkg_install-devel" return 1 fi if [ ! -r "$portaudit_dir/$portaudit_filename" ]; then echo "portaudit: Database missing, run \`portaudit -F' to update." >&2 return 2 elif ! checksum_auditfile; then echo "portaudit: Corrupt database." >&2 return 2 elif ! checkexpiry_auditfile 14; then echo "portaudit: Database too old." >&2 return 2 fi prerequisites_checked=true return 0 } audit_installed() { local rc=0 extract_auditfile | awk -F\| "$PRINTAFFECTED_AWK"' BEGIN { vul=0 } /^(#|\$)/ { next } $2 !~ /'"$opt_restrict"'/ { next } { cmd="'"$pkg_info"' -E \"" $1 "\"" while((cmd | getline pkg) > 0) { vul++ print_affected(pkg) } close(cmd) } END { print vul " problem(s) in your installed packages found." if (vul > 0) { print "\nYou are advised to update or deinstall" \ " the affected package(s) immediately." exit(1) } } ' || rc=$? return $rc } audit_file() { local rc=0 local TMPFILE= case "$1" in -) TMPFILE=`mktemp -t portaudit` cat > "$TMPFILE" FILE="$TMPFILE" ;; http://*|ftp://*|https://*|file://*) echo "portaudit: Can't audit remote file $1" >&2 return 2 ;; *) if [ -r "$1" ]; then FILE="$1" else echo "portaudit: Can't read $1" >&2 return 2 fi ;; esac extract_auditfile | awk -F\| "$PRINTAFFECTED_AWK"' BEGIN { vul=0 } /^(#|\$)/ { next } { cmd="'"$pkg_version"' -T - \"" $1 "\" <\"'"$FILE"'\"" while((cmd | getline pkg) > 0) { if ($2 !~ /'"$opt_restrict"'/) next vul++ print_affected(pkg) } close(cmd) } END { print vul " problem(s) found." if (vul > 0) { exit(1) } } ' || rc=$? if [ -n "$TMPFILE" ]; then rm "$TMPFILE" fi return $rc } audit_args() { local VULCNT=0 while [ $# -gt 0 ]; do case "$1" in /*|-) echo "portaudit: $1 is a file, please use the -f option" >&2 ;; http://*|ftp://*|https://*|file://*) echo "portaudit: Can't audit remote file $1" >&2 ;; *) if VLIST=`extract_auditfile | awk -F\| ' /^(#|\$)/ { next } $2 !~ /'"$opt_restrict"'/ { next } { print } ' | $pkg_version -T "$1" -`; then VULCNT=$(($VULCNT+1)) echo "$VLIST" | awk -F\| "$PRINTAFFECTED_AWK"' { print_affected("'"$1"'") } ' fi ;; esac shift done $opt_quiet || echo "$VULCNT problem(s) found." if [ $VULCNT -gt 0 ]; then return 1 fi } audit_cwd() { if [ ! -r "Makefile" ]; then echo "portaudit: No Makefile here" >&2 return 2 fi PKGNAME=`make -VPKGNAME 2>/dev/null || true"` if [ -z "$PKGNAME" ]; then echo "portaudit: Can't determine the package name" >&2 return 2 fi if VLIST=`extract_auditfile | awk -F\| ' /^(#|\$)/ { next } $2 !~ /'"$opt_restrict"'/ { next } { print } ' | $pkg_version -T "$PKGNAME" -`; then echo "$VLIST" | awk -F\| "$PRINTAFFECTED_AWK"' { print_affected("'"$PKGNAME"'") } ' return 1 fi } fetch_auditfile() { local rc=2 if [ ! -d "$portaudit_dir" ]; then if ! mkdir -p "$portaudit_dir"; then echo "Couldn't create $portaudit_dir, try running \`portaudit -F' as root" >&2 return 2 fi fi if [ ! -w "$portaudit_dir" ]; then echo "Couldn't write to $portaudit_dir, try running \`portaudit -F' as root" >&2 return 2 fi cd "$portaudit_dir" if [ -r "$portaudit_filename" ]; then cp -f "$portaudit_filename" "$portaudit_filename.old" fi $opt_verbose && echo "Attempting to fetch from $portaudit_site." urls=`echo "$portaudit_sites" | tr -s ' \t' '\n' | sed -E -e "s/?\$/$portaudit_filename"` if ! env $portaudit_fetch_env $portaudit_fetch_cmd $urls; then echo "Couldn't fetch database." >&2 elif [ ! -f "$portaudit_dir/$portaudit_filename" ] ; then echo "portaudit: No database." >&2 elif ! checksum_auditfile; then echo "portaudit: Database corrupt." >&2 elif ! checkexpiry_auditfile 7; then echo "portaudit: Database too old." >&2 else $opt_quiet || echo "New database installed." rc=0 break fi if [ -f "$portaudit_filename.old" ]; then if [ $rc -eq 0 ]; then rm -f "$portaudit_filename.old" else mv -f "$portaudit_filename.old" "$portaudit_filename" $opt_quiet || echo "Old database restored." fi fi if [ -f "$portaudit_filename" ]; then chmod a=r "$portaudit_filename" fi return $rc } portaudit_confs opt_audit=false opt_auditcwd=false opt_dbversion=false opt_fetch=false opt_file= opt_quiet=false opt_restrict= opt_verbose=false opt_version=false opt_expiry= if [ $# -eq 0 ] ; then opt_audit=true fi while getopts aCdf:Fqr:vVX: opt; do case "$opt" in a) opt_audit=true;; C) opt_auditcwd=true;; d) opt_dbversion=true;; f) opt_file="$OPTARG";; F) opt_fetch=true;; q) opt_quiet=true;; r) opt_restrict="$OPTARG";; v) opt_verbose=true;; V) opt_version=true;; X) opt_expiry="$OPTARG";; ?) echo "Usage: $0 -aCdF [-X days] [-r pattern] [-f file] [pkg-name ...]" exit 2;; esac done shift $(($OPTIND-1)) ret=0 if $opt_version; then echo "portaudit version %%PORTVERSION%%" fi if $opt_fetch; then if ! fetch_auditfile; then echo "portaudit: Download failed." >&2 exit 2 fi elif [ -n "$opt_expiry" ]; then if [ ! -r "$portaudit_dir/$portaudit_filename" ] || ! checkexpiry_auditfile "$opt_expiry"; then $opt_quiet || echo "Downloading fresh database." if ! fetch_auditfile; then echo "portaudit: Download failed." >&2 exit 2 fi ret=1 fi fi if $opt_dbversion; then if [ ! -f "$portaudit_dir/$portaudit_filename" ]; then echo "portaudit: Database missing, run \`portaudit -F' to update." >&2 exit 2 fi if ! checksum_auditfile; then echo "portaudit: Database corrupt." >&2 exit 2 fi created=`getcreated_auditfile` echo "Database created: `date -j -f '%Y-%m-%d %H:%M:%S %Z' \"$created GMT\"`" fi prerequisites_checked=false if $opt_quiet; then PRINTAFFECTED_AWK=' function print_affected(apkg) { print apkg } ' elif $opt_verbose; then PRINTAFFECTED_AWK=' function print_affected(apkg) { split(apkg, thepkg) print "Affected package: " thepkg[1] " (matched by " $1 ")" print "Type of problem: " $3 "." split($2, ref, / /) for (r in ref) print "Reference: <" ref[r] ">" print "" } ' else PRINTAFFECTED_AWK=' function print_affected(apkg) { split(apkg, thepkg) print "Affected package: " thepkg[1] print "Type of problem: " $3 "." split($2, ref, / /) for (r in ref) print "Reference: <" ref[r] ">" print "" } ' fi if $opt_audit; then portaudit_prerequisites audit_installed || ret=$? fi if $opt_auditcwd; then portaudit_prerequisites audit_cwd || ret=$? fi if [ -n "$opt_file" ]; then portaudit_prerequisites audit_file "$opt_file" || ret=$? fi if [ $# -gt 0 ]; then portaudit_prerequisites audit_args "$@" || ret=$? fi exit $ret