Checking for most recent Solaris Security Patches

From: spamhaterat_private
Date: Wed Jan 06 1999 - 07:37:01 PST

  • Next message: kay: "Re: PATH variable in zip-slackware 2.0.35"

    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 <barnettat_private>
    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 <barnettat_private>
    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 <barnettat_private>    -*-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
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 14:27:35 PDT