#!/bin/sh
# $Id: CheckPatches,v 1.25 2001/01/26 15:50:06 reggers Exp $
#
# From a Perl version Copyright 1998 by Bruce Barnett <barnett@grymoire.com>
#
# This program checks for latest recommended, security & Y2K patches on
# a Solaris2.x (or later) system.  Since it uses showrev(1) to determine
# this, and doesn't verify the integrity of the data, it is possible
# to fool this check.
#
# CAVEAT EMPTOR: This software is made available AS IS with no assurance
#		of fitness. No guarantee is given or implied. Nor should
#		any be assumed. Use at your own risk!!
#
# v 1.18 -- Sean Boran <sean@boran.com> adds $user. In combination with
#           $site you can get through an ftp proxy to reach sunsolve.
#
#	eg. CheckPatches -f -s proxy.company.com -u anonymous@sunsolve.sun.com
#
# v 1.19 -- Sean Boran's x86 requirements met.
#
# v 1.25 -- If "<patchno>-<vers>  blah blah" occurs in the Exceptions
#	    file then ignore "<patchno>-*". As requested by Sean Boran.
#
# This sh version Aug/2000 by Reg Quinton <reggers@ist.uwaterloo.ca>. Feel
# free to copy in whole or in part .... but use at your own risk.

umask 022
PATH=/usr/bin:/usr/sbin; export PATH
Usage='CheckPatches [-fvh] [ -s site ][ -u user ][ -d directory]
     -f             fetch a patch report for this OS
     -v             be verbose
     -h             get this help
     -s site        anon-ftp site (default: sunsolve.sun.com
     -u user        anon-ftp user (default: ftp
     -d directory   directory at anon-ftp site (default: /pub/patches'

# command line options - modified by command line arguments

site=sunsolve.sun.com			# where to get the patch report
patches=/pub/patches			# where I find things there
user=ftp				# the "anonymous" account
pass=$LOGNAME@`hostname`		# for anon-ftp login
vulnerable=				# is the system vulnerable?
verbose=				# be noisy?
fetch=					# fetch a patch report?

# Solaris Revision from SunOS release number...

SolRev=`uname -r |sed -e 's/^5.\([0-6]\)/2.\1/' -e 's/^5.\([7-9]\)/\1/'`;

# if not sparc then assume intel x86 report

[ `uname -p` = "i386" ] && SolRev=${SolRev}_x86

# Important file names

PatchReport="Solaris$SolRev.PatchReport"	# entire patch report
IgnPatches="${PatchReport}.Except";		# patches I can ignore
[ -r $IgnPatches ] || IgnPatches=/dev/null	#  ... default is nothing

# Constructed directory on /tmp

Tmp=/tmp/CheckPatches.$$
	rm -rf "$Tmp" 2>/dev/null; mkdir "$Tmp"
	trap "cd /tmp; rm -rf $Tmp 2>/dev/null; exit 255"  1 2 3 13 15
SecPatches=$Tmp/SecPatches			# Security Patches
RecPatches=$Tmp/RecPatches			# Recommended Patches
Y2KPatches=$Tmp/Y2KPatches			# Y2000 Patches
AwkPatches=$Tmp/AwkPatches			# awk filter to toss some

# fatal error -- moan and die 

die () {
    [ "$*" ] && echo "$*" 1>&2 
    kill -1 $$
}

# fetch a patch report file from sunsolve.

Fetch () {
    file="$*"
    [ "$verbose" ] && echo "Fetching ftp://$site$patches/$file\n";

    ftp -n -i $site 2>/dev/null <<EOF
user $user $pass
cd  $patches
get $file
EOF
     [ $? != 0 ]    && die "Failed to fetch $file from $site"
     [ -r "$file" ] || die "Failed to fetch $file from $site"
}

# parse command line options 

while getopts vfhs:u:d: c; do
    case $c in
	v) verbose=true     ;;
	f) fetch=true       ;;
        s) site=$OPTARG     ;;
        u) user=$OPTARG     ;;
        d) patches=$OPTARG  ;;
	*) die "$Usage"     ;;
    esac
done

# I should have no arguments left

shift `expr $OPTIND - 1`; [ "$*" ] && die "$Usage"

# If you asked me to fetch the patch report I will ....
#	 If you don't have one I will get it even if you didn't ask.

[ "$fetch" ]          && Fetch $PatchReport
[ -r "$PatchReport" ] || Fetch $PatchReport

# Construct an awk filter to toss patches that have been applied or have
# been listed in an exceptions file.. Note that I ignore the release number
# of patches in the Exception list. I only care about the patch no.
 
[ "$verbose" ] && echo "Constructing exception list"
( sed -n 's/^ *\([0-9]\{1,\}-\).*/\1/p' $IgnPatches;
  showrev -p | awk '{print $2}' ) | sort -u |
  sed -e 's:\(.*\):/^\1/ {next;}:' > $AwkPatches
 
echo '{print}' >> $AwkPatches

# The Patch report we tear apart into Y2K, Security & Recommended patches.
# But on the fly we toss ones we can ignore (because they're applied or
# we've been instructed to).

[ "$verbose" ] && echo "Constructing Y2K patch list"
sed -n '/^Solaris.* Patches Containing Y2000/,//p' \
	$PatchReport | sed -n '/^[0-9]\{1,\}-[0-9]\{1,\}/p' | \
	awk -f $AwkPatches > $Y2KPatches
[ "$verbose" ] && echo "Constructing Security patch list"
sed -n '/^Solaris.* Patches Containing Security Fixes/,//p' \
	$PatchReport | sed -n '/^[0-9]\{1,\}-[0-9]\{1,\}/p' | \
	awk -f $AwkPatches > $SecPatches
[ "$verbose" ] && echo "Constructing Recommended patch list"
sed -n '/^Solaris.* Recommended Patches/,//p' \
	$PatchReport | sed -n '/^[0-9]\{1,\}-[0-9]\{1,\}/p' | \
	awk -f $AwkPatches > $RecPatches

# Any missing patches I ought to moan about?
 
if [ -s $SecPatches ]; then
      vulnerable=true
      echo "Missing Security Patches for Solaris$SolRev\n"
      cat $SecPatches
      echo
fi

if [ -s $RecPatches ]; then
      vulnerable=true
      echo "Missing Recommended Patches for Solaris$SolRev\n"
      cat $RecPatches
      echo
fi

if [ -s $Y2KPatches ]; then
      vulnerable=true
      echo "Missing Y2000 Patches for Solaris$SolRev\n"
      cat $Y2KPatches
      echo
fi
 
# Toss temporary files and exit with a reasonable status

cd /tmp; rm -rf $Tmp 2>/dev/null

if [ "$vulnerable" ]; then
    echo "For more information see '$PatchReport'"
    exit 255
fi

# Ok, you win. Everything is clear.

echo "OK: patch level"
exit 0
