[8967] in bugtraq

home help back first fref pref prev next nref lref last post

Checking for most recent Solaris Security Patches

daemon@ATHENA.MIT.EDU (spamhater@GRYMOIRE.COM)
Wed Jan 6 14:04:59 1999

Date: 	Wed, 6 Jan 1999 10:37:01 -0500
Reply-To: spamhater@GRYMOIRE.COM
From: spamhater@GRYMOIRE.COM
To: BUGTRAQ@NETSPACE.ORG

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 <barnett@grymoire.com>
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 <barnett@grymoire.com>
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 <barnett@grymoire.com>    -*-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

home help back first fref pref prev next nref lref last post