mirror of
https://github.com/opinkerfi/nagios-plugins.git
synced 2024-11-05 01:53:44 +01:00
288 lines
11 KiB
Perl
Executable File
288 lines
11 KiB
Perl
Executable File
#! /usr/bin/perl -w
|
|
################################################################################
|
|
# check_snmp_patchlevel.pl v1.2
|
|
#
|
|
# Nagios plugin to check the OS version string through SNMP sysDescr value.
|
|
# Please adjust the line setting the "use lib" path so utils.pm can be found.
|
|
# @2009 public[at]frank4dd[dot]com
|
|
#
|
|
# updates:
|
|
# 20090228 v1.1 adds support for Cisco PIX
|
|
# 20091117 v1.2 adds support for selection of SNMP version 1 or 2
|
|
################################################################################
|
|
|
|
use strict;
|
|
use Getopt::Long;
|
|
use Net::SNMP;
|
|
|
|
use vars qw($opt_V $opt_v $opt_h $opt_H $opt_g $opt_C $opt_f $PROGNAME @raw_data $output);
|
|
use lib "/usr/lib/nagios/plugins" ;
|
|
use utils qw(%ERRORS &print_revision &support &usage);
|
|
|
|
$PROGNAME = "check_snmp_patchlevel.pl";
|
|
|
|
sub print_help ();
|
|
sub print_usage ();
|
|
|
|
$ENV{'PATH'}='';
|
|
$ENV{'BASH_ENV'}='';
|
|
$ENV{'ENV'}='';
|
|
|
|
#print("Args: $#ARGV\n");
|
|
if ($#ARGV == -1) { usage("Missing arguments\.\nUsage: $PROGNAME -H <host> [-v snmp version 1|2] -g <ios|asa|pix> [-C community] [-f <config file>]\n"); }
|
|
|
|
Getopt::Long::Configure('bundling');
|
|
GetOptions
|
|
("V" => \$opt_V, "plugin-version"=> \$opt_V,
|
|
"v=s" => \$opt_v, "snmp-version=s"=> \$opt_v,
|
|
"h" => \$opt_h, "help" => \$opt_h,
|
|
"H=s" => \$opt_H, "hostname=s" => \$opt_H,
|
|
"C=s" => \$opt_C, "community=s" => \$opt_C,
|
|
"f=s" => \$opt_f, "configfile=s" => \$opt_f,
|
|
"g=s" => \$opt_g, "devicegroup=s" => \$opt_g);
|
|
|
|
if ($opt_V) {
|
|
print_revision($PROGNAME,'$Revision: 120 $');
|
|
exit $ERRORS{'OK'};
|
|
}
|
|
|
|
if ($opt_h) { print_help(); }
|
|
|
|
# The SNMP port defaults to 161. It is not made a commandline option yet.
|
|
my $opt_p = 161;
|
|
# The SNMP timeout is set to 5 seconds. It is not made a commandline option yet.
|
|
my $opt_t = 5;
|
|
|
|
################################################################################
|
|
# Check for the "required" options -H <host name or IP>, -g <ios|asa|pix>
|
|
################################################################################
|
|
|
|
($opt_H) || usage("Host name/address not specified\. Use $PROGNAME -h for help\.\n");
|
|
my $host = $1 if ($opt_H =~ /([-.A-Za-z0-9]+)/);
|
|
($host) || usage("Invalid host: $opt_H\n");
|
|
|
|
($opt_g) || usage("Device type not specified\. Use $PROGNAME -h for help\.\n");
|
|
# no sanity chck here, it is done below where $opt_g is parsed.
|
|
|
|
################################################################################
|
|
# Check for the "optional" options -v -C, -f, -s
|
|
################################################################################
|
|
# set the SNMP version or assume it is "1" (default)
|
|
($opt_v) || ($opt_v = 1) ;
|
|
if ( $opt_v ne 1 && $opt_v ne 2) {
|
|
printf("UNKNOWN: SNMP version $opt_v.\n");
|
|
exit $ERRORS{'UNKNOWN'};
|
|
}
|
|
|
|
# set the SNMP community or assume it is "public" (default)
|
|
($opt_C) || ($opt_C = "public") ;
|
|
|
|
# load config file
|
|
if($opt_f) { &read_config(); }
|
|
|
|
################################################################################
|
|
# We fetch the system description via SNMP. It should contain the OS version.
|
|
# The SNMP OID we query is .1.3.6.1.2.1.1.1.0 = SNMPv2-MIB::sysDescr.0
|
|
# $version = `/usr/bin/snmpget -OQv -v 1 -c $opt_C $host .1.3.6.1.2.1.1.1.0`;
|
|
################################################################################
|
|
my $snmpdata="";
|
|
my $response="";
|
|
my $sysdesc_oid = ".1.3.6.1.2.1.1.1.0";
|
|
|
|
my ($session, $error) = Net::SNMP->session(
|
|
-hostname => $opt_H,
|
|
-community => $opt_C,
|
|
-port => $opt_p,
|
|
-timeout => $opt_t,
|
|
-version => $opt_v
|
|
);
|
|
|
|
if(!defined($session)) { printf("UNKNOWN: %s.\n", $error); exit $ERRORS{'UNKNOWN'}; }
|
|
|
|
$response = $session->get_request( -varbindlist => [$sysdesc_oid]);
|
|
|
|
if(!defined($response)) {
|
|
printf("UNKNOWN: %s.\n", $session->error);
|
|
$session->close;
|
|
exit $ERRORS{'UNKNOWN'};
|
|
}
|
|
|
|
$snmpdata = $response->{$sysdesc_oid};
|
|
$session->close;
|
|
|
|
################################################################################
|
|
# There is no standardized way for displaying OS version and patch levels.
|
|
# Depending on the vendor and OS, we need to parse this data differently.
|
|
# We don't have a switch statement yet (coming in perl 5.10), so we need
|
|
# to cascade the if-thens
|
|
################################################################################
|
|
|
|
################################################################################
|
|
# Example response for Cisco Routers, here a from a Cat-6506:
|
|
# Version: Cisco Internetwork Operating System Software
|
|
# IOS (tm) MSFC2 Software (C6MSFC2-JSV-M), Version 12.1(27b)E3, RELEASE SOFTWARE (fc1)
|
|
# ^^^^^^^^^^^
|
|
# Technical Support: http://www.cisco.com/techsupport
|
|
# Copyright (c) 1986-2007 by cisco Systems, Inc.
|
|
# Compiled Tue 07-Aug-0
|
|
# Example response for Cisco Routers, here a from a Cat-3750:
|
|
# Cisco IOS Software, C3750 Software (C3750-IPBASE-M), Version 12.2(25)SEE2, RELEASE SOFTWARE (fc1)
|
|
# ^^^^^^^^^^^^
|
|
# Copyright (c) 1986-2006 by Cisco Systems, Inc.
|
|
# Compiled Fri 28-Jul-06 08:46 by yenanh
|
|
################################################################################
|
|
my $version="";
|
|
|
|
if ($opt_g =~ /ios/) {
|
|
my $line="";
|
|
my @lines = split ('\n', $snmpdata);
|
|
foreach $line (@lines) {
|
|
if($line =~ /IOS .*/) {
|
|
(my @fields) = split(', ', $line);
|
|
foreach my $field (@fields) {
|
|
if($field =~ /Version/) { (my $txt, $version) = split('Version ', $field); }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
################################################################################
|
|
# Example response for Cisco ASA 5520:
|
|
# "Cisco Adaptive Security Appliance Version 8.0(4)"
|
|
# ^^^^^^
|
|
################################################################################
|
|
elsif ($opt_g =~ /asa/) {
|
|
if($snmpdata =~ /^Cisco Adaptive Security Appliance.*/) {
|
|
(my $os, $version) = split('Version ', $snmpdata);
|
|
}
|
|
}
|
|
|
|
###############################################################################
|
|
# Example response for Cisco PIX 525:
|
|
# "Cisco PIX Firewall Version 6.3(5)"
|
|
# "Cisco Cisco PIX Security Appliance Version 7.2(2)"
|
|
# "Cisco Cisco PIX Security Appliance Version 8.0(2)"
|
|
###############################################################################
|
|
elsif ($opt_g =~ /pix/) {
|
|
if($snmpdata =~ /.* PIX .*/) {
|
|
(my $os, $version) = split('Version ', $snmpdata);
|
|
}
|
|
}
|
|
|
|
else { usage("Unknown option parameter: $opt_g\. Use $PROGNAME -h for help\.\n"); }
|
|
|
|
################################################################################
|
|
# If the SNMP data cannot be parsed, we generate the exit code and finish.
|
|
################################################################################
|
|
if($version eq "") {
|
|
printf("UNKNOWN: cannot find version string in SNMP response. Either -t <type> is incorrect or the Version string is unreadable.\n");
|
|
exit $ERRORS{'UNKNOWN'};
|
|
}
|
|
################################################################################
|
|
# We are in 'discovery' mode, we report the OS Version, return 'OK' and finish.
|
|
################################################################################
|
|
elsif (! $opt_f) {
|
|
printf (uc($opt_g)." Version: $version | $snmpdata\n");
|
|
exit $ERRORS{'OK'};
|
|
}
|
|
################################################################################
|
|
# We are in 'compliance' mode, we check the OS Version against the config file
|
|
################################################################################
|
|
else {
|
|
foreach my $line (@raw_data) {
|
|
# skip comment lines
|
|
next if($line =~ /^#.*$/);
|
|
chomp($line);
|
|
|
|
(my $required, my $osgroup, my $osversion, my $remarks)=split(/\|/,$line);
|
|
|
|
if( ($opt_g eq $osgroup) && ($version eq $osversion) ) {
|
|
|
|
if($required eq "approved") {
|
|
$output = uc($opt_g)." Version: $version approved";
|
|
if ($remarks ne "") { $output = $output." | Remarks: ".$remarks." Data: $snmpdata\n"; }
|
|
else { $output = $output." | $snmpdata\n"; }
|
|
printf $output; exit $ERRORS{'OK'};
|
|
}
|
|
|
|
if($required eq "obsolete") {
|
|
$output = uc($opt_g)." Version: $version obsolete";
|
|
if ($remarks ne "") { $output = $output." | Remarks: ".$remarks." Data: $snmpdata\n"; }
|
|
else { $output = $output." | $snmpdata\n"; }
|
|
printf $output; exit $ERRORS{'WARNING'};
|
|
}
|
|
|
|
if($required eq "med-vuln") {
|
|
$output = uc($opt_g)." Version: $version vulnerable (low-medium)";
|
|
if ($remarks ne "") { $output = $output." | Remarks: ".$remarks." Data: $snmpdata\n"; }
|
|
else { $output = $output." | $snmpdata\n"; }
|
|
printf $output; exit $ERRORS{'WARNING'};
|
|
}
|
|
|
|
if($required eq "crit-vuln") {
|
|
$output = uc($opt_g)." Version: $version vulnerable (high risk)";
|
|
if ($remarks ne "") { $output = $output." | Remarks: ".$remarks." Data: $snmpdata\n"; }
|
|
else { $output = $output." | $snmpdata\n"; }
|
|
printf $output; exit $ERRORS{'CRITICAL'};
|
|
}
|
|
}
|
|
}
|
|
# the OS version is not listed, we don't know exactly if its good or bad.
|
|
printf (uc($opt_g)." Version: $version unverified | $snmpdata\n");
|
|
exit $ERRORS{'UNKNOWN'};
|
|
}
|
|
################################################################################
|
|
# End of main. Subroutine definitions below.
|
|
################################################################################
|
|
sub read_config () {
|
|
|
|
if (! -e $opt_f) { usage("Cannot find file $opt_f\. Check file name and path\.\n"); }
|
|
open(DATFILE, $opt_f) || usage("Cannot read file $opt_f\. Check permissions\.\n");
|
|
@raw_data=<DATFILE>;
|
|
close(DATFILE);
|
|
}
|
|
|
|
sub print_usage () {
|
|
print "Usage: $PROGNAME -H <host> [-v snmp version 1|2] -g <ios|asa|pix> [-C community] [-f <config file>]\n";
|
|
exit $ERRORS{'OK'};
|
|
}
|
|
|
|
sub print_help () {
|
|
print_revision($PROGNAME,'$Revision: 1.0.0 $');
|
|
print "Copyright (c) 2009 Frank4DD
|
|
|
|
This plugin intends to report the OS version string of a supported vendor.
|
|
Currently it is parsing Cisco IOS, PIX and ASA versions through SNMP sysDescr polls.
|
|
|
|
In 'discovery mode' without using the option '-f', this plugin returns the OS
|
|
version string with 'OK' if the version could be fetched, or 'UNKNOWN' if not.
|
|
Useful if you need a view on what is out there in a enterprise environment.
|
|
|
|
In 'compliance mode' by specifying '-f', this plugin compares the device OS
|
|
version string against a list of categorized (approved) OS versions. It is
|
|
meant to identify devices running a rogue, obsolete or vulnerable OS version.
|
|
|
|
Usually this check runs only once in a couple of hours or even longer periods.
|
|
The configuration file format is in the check_snmp_patchlevel.cfg template.
|
|
|
|
";
|
|
print_usage();
|
|
print "
|
|
-H, --hostname=HOST
|
|
Name or IP address of host to check
|
|
-v, --snmp-version [1|2]
|
|
Specify the SNMP version to use: 1 or 2c
|
|
-g, --devicegroup=[ios|asa]
|
|
OS version string to expect: ios = Cisco IOS Routers
|
|
asa = Cisco ASA Appliances
|
|
pix = Cisco PIX Firewalls
|
|
-C, --community=community
|
|
SNMP community (default public)
|
|
-f, --configfile=STRING
|
|
Version file and path, contains a list of approved versions
|
|
|
|
";
|
|
support();
|
|
exit $ERRORS{'OK'};
|
|
}
|