initial version
authorChristian Arnold <arnold@schlittermann.de>
Mon, 14 Sep 2009 16:08:35 +0200
changeset 0 c163c53b5be4
child 1 acac1ec01fd8
initial version
.perltidyrc
Makefile
check-cert.pl
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.perltidyrc	Mon Sep 14 16:08:35 2009 +0200
@@ -0,0 +1,2 @@
+--paren-tightness=2
+--square-bracket-tightness=2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Mon Sep 14 16:08:35 2009 +0200
@@ -0,0 +1,14 @@
+SCRIPT = check-cert
+
+.PHONY:	all clean
+
+all:	$(SCRIPT)
+
+clean:
+	rm -rf $(SCRIPT)
+
+%:	%.pl Makefile
+	# $@
+	@perl -c $<
+	@cp -f $< $@
+	@chmod a=rx $@
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/check-cert.pl	Mon Sep 14 16:08:35 2009 +0200
@@ -0,0 +1,298 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+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 lib "/usr/lib/nagios/plugins";
+use utils qw (%ERRORS &print_revision &support);
+
+my $ME        = basename $0;
+my $VERSION   = "0.4";
+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_debug,    $opt_init,   $opt_signature,
+    $opt_version,  $opt_help,   $opt_warning, $opt_directory,
+    $opt_critical, $opt_binary, @opt_exclude
+);
+my ($file, $w_time, $c_time);
+
+my (@critical, @warning);
+
+$opt_binary    = "/usr/bin/openssl";
+$opt_signature = "md5WithRSAEncryption";
+$opt_warning   = "1month";
+$opt_critical  = "1week";
+$opt_directory   = "/etc";
+
+sub process_file();
+sub print_help();
+sub print_usage();
+
+MAIN: {
+
+    Getopt::Long::Configure('bundling');
+    GetOptions(
+        "i|init"       => \$opt_init,
+        "h|help"       => \$opt_help,
+        "V|version"    => \$opt_version,
+        "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,
+    );
+
+    print_revision($ME, $VERSION) and exit $ERRORS{"OK"} if $opt_version;
+    print_help() and exit $ERRORS{"OK"} if $opt_help;
+
+    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;
+
+    find({ wanted => \&process_file }, $opt_directory);
+
+    # 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
+    @opt_exclude = split(/,/, join(',', @opt_exclude)) if @opt_exclude;
+    foreach my $exclude_file (@opt_exclude) {
+        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 print_usage() {
+    print "Usage:\n";
+    print
+"  $ME [-b <binary>] [-i init] [-d <path>] [-w <time>] [-c <time>] [-s <signature algorithm>] [-e <file,file,file,...>]\n";
+    print "  $ME [-h | --help]\n";
+    print "  $ME [-V | --version]\n";
+}
+
+sub print_help() {
+    print_revision($ME, $VERSION);
+    print "Copyright (c) 2009 Christian Arnold\n\n";
+    print "This plugin checks the expire date for openssl certificates.\n\n";
+    print_usage();
+    print "\n";
+    print "  -b, --binary <binary>\n";
+    print "     Path of openssl binary (default: /usr/bin/openssl)\n";
+	print "  -d, --directory <path>\n";
+	print "     Absolute directory path in which will be recursively search for certificate files (default: /etc).\n";
+    print "  -w, --warning <time>\n";
+    print
+"     Certificat should not be more than this time older (default: 1month).\n";
+    print
+"     For time can be used year, month, day, hour, minute, second and weeks.\n";
+    print "  -c, --critical <time>\n";
+    print
+"     Certificat should not be more than this time older (default: 1week)\n";
+    print
+"     For time can be used year, month, day, hour, minute, second and weeks.\n";
+    print "  -s, --signature <signature algorithm>\n";
+    print
+"     Return WARNING status if <signature algorithm> is used (default: md5WithRSAEncryption).\n";
+    print "  -e, --exclude <file,file,file, ...>\n";
+    print
+"     Absolute path of excluded files, use comma-separated lists for multiple files.\n";
+    print "  -h, --help\n";
+    print "     Print detailed help screen\n";
+    print "  -V, --version\n";
+    print "     Print version information\n";
+    print "\n";
+    support();
+}