#!/usr/bin/perl -w
# $Id$
# $URL$

use strict;
use warnings;
use File::Basename;
use Getopt::Long;
use Date::Manip;
use IPC::Open2;
use lib "/usr/lib/nagios/plugins";
use utils qw (%ERRORS &print_revision &support);

sub print_help();
sub print_usage();

my $ME = basename $0;
my ($opt_w, $opt_c, $opt_V, $opt_h, $opt_b, @opt_certfiles);
my ($w_time, $c_time, $result, $message, %certs);
my (@critical, @warning);

$opt_w = "1month";
$opt_c = "1week";
$opt_b = "/usr/bin/openssl";

Getopt::Long::Configure('bundling');
GetOptions(
    "V"   => \$opt_V, "version"    => \$opt_V,
    "h"   => \$opt_h, "help"       => \$opt_h,
    "b=s" => \$opt_b, "binary"     => \$opt_b,
    "w=s" => \$opt_w, "warning=s"  => \$opt_w,
    "c=s" => \$opt_c, "critical=s" => \$opt_c,
    "f=s" => \@opt_certfiles, "certfile=s" => \@opt_certfiles);

if ($opt_V) {
    print_revision($ME, "0.1");
    exit $ERRORS{"OK"};
}

if ($opt_h) {
    print_help();
    exit $ERRORS{"OK"};
}

# check openssl binary
unless (-x $opt_b) {
    print "CERT CRITICAL: OpenSSL not found or not executable - $opt_b\n";
    exit $ERRORS{"CRITICAL"};
}

unless(@opt_certfiles) {
    print "CERT WARNING: Not defined any certificate files\n";
    exit $ERRORS{"WARNING"};
}

@opt_certfiles = split(/,/, join(',', @opt_certfiles));

# extract certificate data
foreach my $file (@opt_certfiles) {
    unless (-r $file) {
	print "CERT CRITICAL: $file - not exists or not read permission is granted\n";
	exit $ERRORS{"CRITICAL"};
    }
    my @cmd_x509 = ($opt_b, "x509", "-in", $file, "-noout", "-subject", "-enddate");
    my @cmd_pkcs12 = ($opt_b, "pkcs12", "-in", $file, "-clcerts", "-nokeys", "-nomacver", "-passin", "pass:");
    my @cmd_pipe = ($opt_b, "x509", "-noout", "-subject", "-enddate");
    my ($temp, $cn, $enddate, $rc);
    open(CERT, "-|") or do {
	open(STDERR, ">&STDOUT");
	exec(@cmd_x509);
    };

    # check x509 certificates
    while(<CERT>) {
	/unable to load certificate/ and $rc = 1 and last;
	/^subject=\s.*CN=(.*)\s+$/ and $cn = $1;
	/^notAfter=(.*)\s+$/ and $enddate = $1;
    }
    close(CERT);

    # check pkcs12 certificates
    if ($rc) {
	open(PKCS12, "@cmd_pkcs12 |");

	while(<PKCS12>) {
	    $temp .= $_;
	}
	close(PKCS12);

	local (*READ, *WRITE);
	open2(\*READ, \*WRITE,  @cmd_pipe) or die "Can't fork: $!\n";
	print WRITE $temp;
	close(WRITE);

	while(<READ>) {
	    /unable to load certificate/ and print "CERT CRITICAL: unable to load certificate\n" and exit $ERRORS{"CRITICAL"};
	    /^subject=\s.*CN=(.*)\s+$/ and $cn = $1;
	    /^notAfter=(.*)\s+$/ and $enddate = $1;
	}
	close(READ);
    }
    # fill the hash
    push ( @{$certs{$file}}, ($cn, $enddate) );
}

# calculate the time
$w_time = DateCalc("today", "+ $opt_w");
$c_time = DateCalc("today", "+ $opt_c");

# 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] eq "WARNING") {
	push (@warning, "file: $_, CN=@{$certs{$_}}[0] expires @{$certs{$_}}[1]");
    } elsif (@{$certs{$_}}[2] eq "CRITICAL") {
	push (@critical, "file: $_, CN=@{$certs{$_}}[0] expires @{$certs{$_}}[1]");
    }
}

# return the state
if (@critical) {
    print "CERT CRITICAL: @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"};
}

sub print_usage() {
    print "Usage:\n";
    print "  $ME [-b <binary>] [-w <time>] [-c <time>] [-f <file,file,file,...>]\n";
    print "  $ME [-h | --help]\n";
    print "  $ME [-V | --version]\n";
}

sub print_help() {
    print_revision($ME, "0.1");
    print "Copyright (c) 2008 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 "  -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 "  -f, --certfile <file,file,file, ...>\n";
    print "     Absolute path of x509 or pkcs12 openssl certificate 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();
}


exit;

# vim:sts=4 sw=4 aw ai sm:
