--- /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 <http://www.gnu.org/licenses/>.
+#
+# Christian Arnold <arnold@schlittermann.de>
+
+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 (<FILE>) {
+ /^$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 (<READ>) {
+ /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 (<FILE>) {
+
+ # 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 (<READ>) {
+ /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 <signature algorithm> 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<check_cert>.
+
+=head1 AUTHOR
+
+Written by Christian Arnold L<arnold@schlittermann.de>
+
+=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