Date: Wed, 6 Jan 1999 10:37:01 -0500
From: [email protected]
To: [email protected]Subject: Checking for most recent Solaris Security Patches
Enclosed is a script that checks if your Solaris system has the latest security patches applied.
It FTP's the status file from Sun's site, caches a local copy, does a
"showrev -a", compares the two, and reports out-of-date patches.
This is not a perfect test, but it is quick and easy, and some might find
it useful.
If the local copy is old, you either get a warning, or a forced update
of the patch information file. (A configuration option).
The package requires expect and perl 5 (no perl modules need be installed).
The enclosed README file has more information.
Cheers
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of shell archive."
# Contents: README CheckPatches Fetch.expect
# Wrapped by barnett@grymoire on Wed Jan 6 10:28:09 1999
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f README -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"README\"
else
echo shar: Extracting \"README\" \(1678 characters\)
sed "s/^X//" >README <<'END_OF_README'
XIt is designed to server the following purpose:
X
X Check if the recent Solaris security patches have been applied.
X If not, it reports those missing patches to standard output
X
XSample output looks like this:
X
XPatch 103959-6 should be updated to 103959-8
XPatch 103743-1 missing
X
XUsage: ./CheckPatches [-vdaf]
X -v => Enable Verbose mode
X -d => Enable Debug mode
X -f => Force fetches the security report file for this Operating System
X -a => Fetches ALL of the security report files (assumes -f)
X
XRequires:
X
X perl
X expect
X
X Ability to ftp to sunsolve.sun.com
X
XFunction:
X
X The program FTP's the appropriate security file from sunsolve.sun.com.
XThe file is cached on the local file system. If the file is old, you
Xeither get a warning, or a forced update. (The two values can be modified).
X
XIf you want to, you can force the system to FTP the report file
Xwith the -f option. If you want to FTP all of the report files for ALL
Xflavors of Solaris, use -a.
X
XInstallation:
X
X 1. Make sure perl and expect are installed. I use perl 5, but
X do not require any modules to be installed.
X 2. Examine CheckPatches and modify the variables at
X the top of the script.
X 3. If ftp does not work without a proxy, you may have
X to modify the Fetch.expect script. Perhaps use the socks
X version of ftp.
X
X
X
XWarning:
X
X This script bases it's decision on the file from Sun's FTP
X site, and the results of the showrev command. Neither of these
X sources are authenticated, and either may be
X spoofed/intercepted/modified/etc.
X
X This package is offered AS IS, with no guarantee or
X warrantee. Use at your own risk.
X
XComments/enhancements are welcome.
XBruce Barnett <[email protected]>
END_OF_README
if test 1678 -ne `wc -c <README`; then
echo shar: \"README\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f CheckPatches -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"CheckPatches\"
else
echo shar: Extracting \"CheckPatches\" \(6977 characters\)
sed "s/^X//" >CheckPatches <<'END_OF_CheckPatches'
X#!/bin/sh -- # perl
Xeval 'exec perl -S $0 ${1+"$@"}' # wish I were -*-Perl-*-
X if 0;
X#!/usr/bin/perl
X#
X# @(#)CheckPatches 1.9 01/05/99
X# Maintained by Bruce Barnett <[email protected]>
X# Please send all updates to the above address
X#
X# This program checks if the latest security patches have been applied
X# to a Solaris workstation. Since it uses showrev(1) to determine
X# this, and doesn't verify the integrity of the data, it is possible
X# to fool this check. A more complete check would examine each file
X# in the patch, and verify that file.
X#
X# This software is offered AS IS. No guarrantees are implied.
X# Use at your own risk
X#
X# This package requires expect and perl to be available and in the searchpath
X#
X
Xeval('use strict'); # Use eval, In case it is not there
X
X##############################
X# You must specify your hostname so that anonymous FTP works.
Xmy $HOSTNAME="hostname.com";
X
X# External Commands used (you may need to modify these);
Xmy $UNAME="uname -a";
Xmy $SHOWREV = "showrev -a";
Xmy $REMOTE="sunsolve.sun.com";
X#my $REMOTE="sunsolve1.sun.com";
Xmy $E_FETCH = "./Fetch.expect"; # Name of the Expect script
X
X# User e-mail name used for password with anonymous FTP
Xmy $EMAIL = $ENV{"LOGNAME"} . "@". $HOSTNAME;
X
X# How old can the local report file be?
X$somewhat_old = 3; # Number of days before I complain
X$real_old = 10; # Number of days before I force the file to be updated
X
X# End of section of user-specified code
X# You should not have to change anything else
X#
X##############################
X# Global variables
Xmy $program = "CheckPatches"; # Name of the program
X
X##############################
X# command line options - modified by command line arguments
Xmy $debug = 0;
Xmy $verbose = 0;
Xmy $fetch = 0; # fetch the file
Xmy $fetch_all = 0; # fetch all of the files
Xmy $expect = 1; # which method shall I use?
X
Xsub getswitches { # get command line arguments
X # parse arguments
X my ($usage) = 0;
X while ($#ARGV>=0 && $ARGV[0] =~ /^-/) {
X my ($arg) = shift(@ARGV);
X $arg =~ /^-v$/ && ($verbose++,next);
X $arg =~ /^-d$/ && ($debug++,next);
X $arg =~ /^-f$/ && ($fetch++,next);
X $arg =~ /^-a$/ && ($fetch_all++,$fetch++,next);
X $arg =~ /^-/ &&
X (printf(STDERR "Ignoring unknown option '%s'\n", $arg),$usage++,next);
X last;
X }
X while ($#ARGV >=0) {
X printf(STDERR "Ignoring argument %s\n", $ARGV[0]);
X shift (@ARGV);
X $usage++;
X }
X if ($usage) {
X printf(STDERR "Usage: %s [-vdaf]\n", $program);
X printf(STDERR "\t-v => Enable Verbose mode\n");
X printf(STDERR "\t-d => Enable Debug mode\n");
X printf(STDERR "\t-f => Fetch the security report file for this Operating System\n");
X printf(STDERR "\t-a => Fetch ALL of the security report files (assumes -f)\n");
X exit 1;
X }
X}
X
Xsub fetch {
X my ($rev) = @_;
X my $ans;
X # Fetch the file using various mechanisms - eventually
X # Just use expect for now
X # Add more options later
X
X my $pfile;
X
X if ($fetch_all) {
X $pfile= sprintf("Solaris*.PatchReport");
X } else {
X $pfile= sprintf("Solaris%s.PatchReport", $rev);
X }
X if ($expect && -f $E_FETCH) {
X $ans = `$E_FETCH $pfile $EMAIL $REMOTE </dev/null 2>&1`;
X }
X $verbose && printf(STDERR "Expect Results: $ans\n");
X}
X
Xsub main {
X my $uname; # UNIX name
X my $osrev; # OS Revision (e.g. 5.6)
X my $Sosrev; # Solaris Revision (w.g. 2.6)
X my $line; #
X my $file; # The file that contains the patch report
X my $mtime; # Modified time of the above file
X my $patch;
X my $rev;
X my %expect;
X my %found;
X my $i;
X
X (defined($0)) && ($program = $0);
X $uname=`$UNAME`;
X
X # Should I run at all? (I only handle Solaris systems)
X chomp $uname;
X if ($uname =~ /SunOS \S+ (\d+)\.([\d.]+)/) {
X chop $uname;
X $osrev = "$1.$2"; # e.g. 5.5.1
X if ($1 == 4 || $1 == 5) { # SunOS 4 or SunOS 5
X $Sosrev = sprintf("%s.%s", $1-3, $2); # e.g. 2.5.1
X } else {
X $Sosrev=$osrev;
X }
X } else {
X printf(STDERR "I don't know how to check the patches of this system: %s\n", $uname);
X exit 1;
X }
X
X &getswitches();
X
X # The security report is in this file
X $file = "Solaris$Sosrev.PatchReport";
X
X if ($fetch) {
X # Then I already decided! :-)
X } elsif ( ! -f $file) {
X $verbose && printf(STDERR "Cannot find file '%s', fetching....\n", $file);
X $fetch++;
X } else { # file exists. Do I need to fetch?
X $mtime=(stat $file)[9];
X # Get the age of the file (time-$mtime) is in minutes. We want days
X my $file_age = (time-$mtime)/(60*60*24);
X
X if ($file_age > $real_old) {
X $fetch++; # REAL OLD - better fetch
X } elsif ($file_age > $somewhat_old) {
X printf(STDERR "Warning, file '%s' is %4.2f days old\n", $file, $file_age);
X printf(STDERR "Suggestion: use '%s -f' to get more recent version of file %s\n",
X $program, $file);
X } else {
X # Not that old, Only mention it if asked.
X $verbose && printf(STDERR "File '%s' is %4.2f days old\n", $file, $file_age);
X }
X }
X # Do I need to fetch a fresh copy of the security report files?
X if ($fetch) {
X $verbose && printf("Fetching...\n");
X &fetch($Sosrev);
X }
X
X open(P,"$file") || die "cannot open file: $file: $!\n";
X my $in_section=0;
X while (defined($line=<P>)) {
X chop $line;
X if ($line =~ /^\s+ as of\s+(\d\d\/[A-Z][a-z][a-z]\/\d\d)$/) {
X $verbose && printf("Security report file '$file' dated %s\n", $1);
X } elsif ($line =~ /Solaris $Sosrev Patches Containing Security Fixes/) {
X $in_section=1; # start of section
X } elsif ($line =~ /Solaris $Sosrev Obsoleted Patches/) {
X $in_section=0; # send of section
X } elsif ($in_section && $line =~ /^(\d+)-(\d+)/) {
X $patch=$1;
X $rev=$2;
X $expect{$patch}=$rev;
X } else {
X $debug && $verbose && printf(STDERR "I am ignoring this line in file '%s': %s\n",
X $file, $line);
X }
X }
X close(P);
X
X# Okay - now phase 2
X my $patches_read = 0;
X open(R, "$SHOWREV |") || die "cannot open pipe to '$SHOWREV': $!\n";
X while (defined($line=<R>)) {
X if ($line =~ /^Patch:\s(\d+)-(\d+)/) {
X $patches_read++;
X $patch=$1;
X $rev=$2;
X if (!defined($found{$patch})) {
X $found{$patch}=$rev;
X } elsif( $found{$patch} < $rev) {
X $found{$patch}=$rev;
X } else {
X $verbose && $debug && printf("Strange... '$SHOWREV' is listing patches in a strange order. First %s-%s, then %s-%s\n",
X $patch, $found{$patch}, $patch, $rev);
X }
X
X }
X }
X
X if ($patches_read == 0) {
X if ($< != 0) {
X printf(STDERR "Unable to read '$SHOWREV,' Re-execute script as root\n");
X } else {
X printf(STDERR "'$SHOWREV' didn't tell me anything about the installed patches. Strange\n");
X }
X } else {
X foreach $i (sort keys %expect) {
X if (!defined($found{$i})) {
X printf("Patch %d-%d missing\n", $i, $expect{$i});
X } else {
X if ($expect{$i} > $found{$i}) {
X printf("Patch %d-%d should be updated to %d-%d\n",
X $i, $found{$i}, $i, $expect{$i});
X }
X }
X }
X }
X}
X
X
X&main();
X
X
X
END_OF_CheckPatches
if test 6977 -ne `wc -c <CheckPatches`; then
echo shar: \"CheckPatches\" unpacked with wrong size!
fi
chmod +x CheckPatches
# end of overwriting check
fi
if test -f Fetch.expect -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"Fetch.expect\"
else
echo shar: Extracting \"Fetch.expect\" \(858 characters\)
sed "s/^X//" >Fetch.expect <<'END_OF_Fetch.expect'
X#!/bin/sh
X
X# This script FTPs a file from the Sun patch directory using expect
X# and anonymous FTP
X# @(#)Fetch.expect 1.4 01/05/99
X# Bruce Barnett <[email protected]> -*-Shell-script-*-
X#
X# Argument 1= filename
X# Argument 2 = password to use for anonymous ftp
X# Argument 3 = site to FTP the file from
X
XFILE=${1:?'Missing argument 1 - filename'}
XUSER=${2:?'Missing argument 2 - password'}
X#REMOTE=${3:-'sunsolve1.sun.com'}
XREMOTE=${3:?'Missing argument 3 - site name'}
X
Xexpect <<EOF
Xset timeout -1
Xspawn ftp -n $REMOTE
X expect {
X ftp> {send "user ftp\r"}
X }
X expect {
X word: {send "$USER\r"}
X }
X expect {
X ftp> {send "bin\r"}
X }
X expect {
X ftp> {send "cd /pub/patches\r"}
X }
X expect {
X ftp> {send "prompt\r"}
X }
X expect {
X ftp> {send "mget $FILE\r"}
X }
X expect {
X ftp> {send "quit\r"}
X }
XEOF
END_OF_Fetch.expect
if test 858 -ne `wc -c <Fetch.expect`; then
echo shar: \"Fetch.expect\" unpacked with wrong size!
fi
chmod +x Fetch.expect
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0