diff -r acac1ec01fd8 -r fe368de5cf81 check_cert.pl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/check_cert.pl Mon Oct 15 12:36:02 2012 +0200 @@ -0,0 +1,391 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2012 Christian Arnold +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Christian Arnold + +use strict; +use File::Find; +use File::Basename; +use Fcntl; +use DB_File; +use Getopt::Long; +use IPC::Open2; +use Date::Manip; +use POSIX ":sys_wait_h"; +use Pod::Usage; + +delete @ENV{ grep /^LC_/ => keys %ENV }; +$ENV{LANG} = "C"; +$ENV{LC_ALL} = "C"; + +sub process_file(); +sub print_help(); +sub print_usage(); +sub version($$); + +my %ERRORS = ( + OK => 0, + WARNING => 1, + CRITICAL => 2, + UNKNOWN => 3, + DEPENDENT => 4 +); + +my $ME = basename $0; +my $NAME = "CERT"; +my $VERSION = "0.5"; +my $hash_file = "/var/tmp/" . basename($0) . ".known.db"; +my %known; +my %certs = (); +my $no_print = +"no_header,no_version,no_serial,no_validity,no_subject,no_issuer,no_pubkey,no_sigdump,no_extensions"; +my @cmd_x509 = ( + "openssl", "x509", "-noout", "-text", + "-certopt", "$no_print", "-subject", "-enddate" +); +my @cmd_pkcs12 = + ("openssl", "pkcs12", "-clcerts", "-nokeys", "-nomacver", "-passin", "pass:"); +my @cmd_pipe = ( + "openssl", "x509", "-noout", "-text", + "-certopt", $no_print, "-subject", "-enddate" +); + +my %opt = ( + "init" => 0, + "binary" => "/usr/bin/openssl", + "directory" => "/etc", + "signature" => "md5WithRSAEncryption", + "warning" => "1month", + "critical" => "1week", + "excluded" => "", + "debug" => 0 +); + +my ($file, $w_time, $c_time); + +my (@critical, @warning); + +MAIN: { + + Getopt::Long::Configure('bundling'); + GetOptions( + "i|init" => \$opt{init}, + "b|binary=s" => \$opt{binary}, + "d|directory=s" => \$opt{directory}, + "w|warning=s" => \$opt{warning}, + "c|critical=s" => \$opt{critical}, + "s|signature" => \$opt{signature}, + "e|exclude=s" => \$opt{exclude}, + "D|debug" => \$opt{debug}, + "h|help" => sub { pod2usage(-verbose => 1, -exitval => $ERRORS{OK}) }, + "m|man" => sub { pod2usage(-verbose => 2, -exitval => $ERRORS{OK}) }, + "V|version" => sub { version($ME, $VERSION); exit $ERRORS{OK}; } + ) or pod2usage(-verbose => 1, -exitval => $ERRORS{CRITICAL}); + + tie(%known, DB_File => $hash_file, O_RDWR | O_CREAT, 0600) + or die "Couldn't tie hash to file $hash_file: $!; aborting"; + + # initiate file-data hash + %known = () if $opt{init}; + + my @directorys = split(/,/, join(',', $opt{directory})) if $opt{directory}; + find({ wanted => \&process_file }, @directorys); + + # calculate the time + $w_time = DateCalc("today", "+ $opt{warning}"); + $c_time = DateCalc("today", "+ $opt{critical}"); + + # check expire date + foreach (sort keys %certs) { + my $enddate; + if (@{ $certs{$_} }[1] =~ /(\w+\s+\d+\s+\d+:\d+:\d+\s+\d+)/) { + $enddate = $1; + } + $enddate = ParseDate($enddate); + unless ($enddate) { + print "CERT CRITICAL: Can't parse enddate\n"; + exit $ERRORS{"CRITICAL"}; + } + + &Date_Cmp($enddate, $w_time) > 0 and push(@{ $certs{$_} }, "OK"), next; + &Date_Cmp($enddate, $c_time) > 0 + and push(@{ $certs{$_} }, "WARNING"), next; + push(@{ $certs{$_} }, "CRITICAL"); + } + + # looking for stats + foreach (sort keys %certs) { + if (@{ $certs{$_} }[2]) { + if (@{ $certs{$_} }[2] eq "$opt{signature}") { + push(@warning, +"file: $_, CN=@{$certs{$_}}[0] Signature Algorithm: @{$certs{$_}}[2]" + ); + } + } + + if (@{ $certs{$_} }[3] eq "WARNING") { + push(@warning, + "file: $_, CN=@{$certs{$_}}[0] expires @{$certs{$_}}[1]"); + } + elsif (@{ $certs{$_} }[3] eq "CRITICAL") { + push(@critical, + "file: $_, CN=@{$certs{$_}}[0] expires @{$certs{$_}}[1]"); + } + } + + # return the state + if (@critical) { + print "CERT CRITICAL: " . join("\n", @critical) . "\n"; + exit $ERRORS{"CRITICAL"}; + } + elsif (@warning) { + print "CERT WARNING: @warning\n"; + exit $ERRORS{"WARNING"}; + } + else { + print "CERT OK: all certificates in limit\n"; + exit $ERRORS{"OK"}; + } + + untie %known; + + exit; +} + +sub process_file() { + return if not -f; + + my $id = join " ", (stat)[7, 9]; + my $is_certificate = 0; + my $in_cert = 0; + my @cert = (); + my ($rc, $temp, $signature, $subject, $enddate); + + # excluded files + my @excludes = split(/,/, join(',', $opt{exclude})) if $opt{exclude}; + foreach my $exclude_file (@excludes) { + if ($exclude_file eq $File::Find::name) { + $known{$File::Find::name} = $id; + return; + } + } + + return + if exists $known{$File::Find::name} + and $known{$File::Find::name} eq $id; + + # checking for pkcs12 certificates + my @cmd_pkcs12_current = @cmd_pkcs12; + push @cmd_pkcs12_current, "-in", $File::Find::name, "2>/dev/null"; + + my $cid = open(FILE, "@cmd_pkcs12_current |") || die "Can't fork: $!"; + + while () { + /^$cid:error:.*/ and last; + $temp .= $_; + } + close(FILE); + + if ($temp) { + local (*READ, *WRITE); + my $cid = open2(\*READ, \*WRITE, @cmd_pipe) + or die "Can't fork: $!\n"; + print WRITE $temp; + close(WRITE); + while () { + /Signature\sAlgorithm:\s(.*)\s+$/ and $signature = $1; + /^subject=\s+(.*)\s+$/ and $subject = $1; + /^notAfter=(.*)\s+$/ and $enddate = $1; + } + close(READ); + + # waiting for child processes + do { + $cid = waitpid(-1, WNOHANG); + } while $cid > 0; + + $is_certificate = 1; + push(@{ $certs{$File::Find::name} }, ($subject, $enddate, $signature)); + + $known{$File::Find::name} = $id if not($is_certificate); + return; + } + + open(FILE, $File::Find::name) or die "can't open $_: $!"; + + while () { + + # cheking for x509 certificates + if ($in_cert) { + push @cert, $_; + if (/^-----END CERTIFICATE-----$/) { + $in_cert = 0; + + # open filehandles (for read and write) + local (*READ, *WRITE); + my $cid = open2(\*READ, \*WRITE, @cmd_x509) + or die "Can' fork: $!\n"; + print WRITE @cert; + close(WRITE); + @cert = (); + + while () { + /Signature\sAlgorithm:\s(.*)\s+$/ and $signature = $1; + /^subject=\s+(.*)$/ and $subject = $1; + /^notAfter=(.*)\s+$/ and $enddate = $1; + } + close(READ); + + # waiting for child processes + do { + $cid = waitpid(-1, WNOHANG); + } while $cid > 0; + + if ($opt{debug}) { + print "-----\n"; + print "$File::Find::name\n"; + print "Signature Algorithm: $signature\n" if ($signature); + print "subject: $subject\n" if ($subject); + print "enddate: $enddate\n" if ($enddate); + } + + push( + @{ $certs{$File::Find::name} }, + ($subject, $enddate, $signature) + ); + $is_certificate = 1; + next; + } + } + + if (/^-----BEGIN CERTIFICATE-----$/) { + $in_cert = 1; + push @cert, $_; + next; + } + } + + $known{$File::Find::name} = $id if not($is_certificate); + close(FILE); +} + +sub version($$) { + my ($progname, $version) = @_; + + print <<_VERSION; +$progname version $version +Copyright (C) 2012 by Christian Arnold and Schlittermann internet & unix support. + +$ME comes with ABSOLUTELY NO WARRANTY. This is free software, +and you are welcome to redistribute it under certain conditions. +See the GNU General Public Licence for details. +_VERSION +} + +__END__ + +=head1 NAME + +check_chert - nagios plugin to check the expire date for openssl certificates + +=head1 SYNOPSIS + +check_cert [B<-b>|B<--binary>] + +check_cert [B<-d>|B<--directory>] + +check_cert [B<-w>|B<--warning>] + +check_cert [B<-c>|B<--critical>] + +check_cert [B<-s>|B<--signature>] + +check_cert [B<-e>|B<--exclude>] + +check_cert [B<-D>|B<--debug>] + +check_cert [B<-h>|B<--help>] + +check_cert [B<-m>|B<--man>] + +check_cert [B<-V>|B<--version>] + +=head1 OPTIONS + +=over + +=item B<-b>|B<--binary> + +Path to openssl binary (default: /usr/bin/openssl). + +=item B<-w>|B<--warning> + +Certificat should not be more than this time older (default: 1month). + +=item B<-c>|B<--critical> + +Certificat should not be more than this time older (default: 1week). + +=item B<-d>|B<--directory> + +Absolute directory path in which will be recursively search for certificate files (default: /etc). + +=item B<-s>|B<--signature> + +Return WARNING status if is used (default: md5WithRSAEncryption). + +=item B<-e>|B<--exclude> + +Absolute path of excluded files, use comma-separated lists for multiple files. + +=item B<-D>|B<--debug> + +Enable debug mode. + +=item B<-h>|B<--help> + +Print detailed help screen. + +=item B<-m>|B<--man> + +Print manual page. + +=item B<-V>|B<--version> + +Print version information. + +=back + +=head1 DESCRIPTION + +his plugin checks the expire date for openssl certificates. + +=head1 VERSION + +This man page is current for version 0.5 of B. + +=head1 AUTHOR + +Written by Christian Arnold L + +=head1 COPYRIGHT + +Copyright (C) 2012 by Christian Arnold and Schlittermann internet & unix support. +This is free software, and you are welcome to redistribute it under certain conditions. +See the GNU General Public Licence for details. + +=cut