#!/usr/bin/perl

use 5.006;
use strict;
use warnings;

use Getopt::Long;
use Pod::Usage;
use Date::Parse;
use POSIX qw(strftime);

# modified from https://lopsa.org/node/1547
my %opt;
GetOptions(\%opt,
    'critical=i',
    'filename=s',
    'help',
    'man',
    'warning=i',);

foreach my $option (qw{critical warning filename}) {
    if (!defined($opt{$option})) { 
        print STDERR "Missing required option: $option\n";
        pod2usage (-verbose => 1); 
        exit 3;
    }
}
my $filename = $opt{'filename'};
chomp $filename;

if (! -f '/usr/bin/openssl') {
    print "Could not find /usr/bin/openssl\n";
    exit 3;
}

my $certdata = parse_certificate( scalar(`/usr/bin/openssl x509 -in $filename -text 2>/dev/null`) );

if (!defined($certdata)) {
    print "Unable to retrieve cert data";
    exit 3;
}

# make sure certificate validity isn't in the future.
my $check_start_time = time;
if (!defined($certdata->{'not_before'})) {
    print "UNKNOWN: failed to parse Not Before validity\n";
    exit 3;
}
if ($check_start_time < $certdata->{'not_before'}) {
    printf("CRITICAL: certificate not valid until %s\n", scalar(localtime($certdata->{'not_before'})));
    exit 2;
}

# make sure certificate validity isn't in the past.
if (!defined($certdata->{'not_after'})) {
    print "UNKNOWN: failed to parse Not After validitiy\n";
    exit 3;
}
if ($check_start_time > $certdata->{'not_after'}) {
    printf("CRITICAL: certificate expired %s\n", scalar(localtime($certdata->{'not_after'})));
    exit 2;
}

# check for impending expiration.
my $expires_date = strftime('%Y-%m-%d %T', localtime($certdata->{not_after}));
my $expires_in = int(($certdata->{'not_after'} - $check_start_time) / (24*60*60));
if ($expires_in <= $opt{'critical'}) {
    printf("CRITICAL: certificate expires in $expires_in days (%s)\n", $expires_date);
    exit 2;
}
if ($expires_in <= $opt{'warning'}) {
    printf("WARNING: certificate expires in $expires_in days (%s)\n", $expires_date);
    exit 1;
}

# note good certificate.
printf("OK: certificate expires in %d days (%s)\n", $expires_in, $expires_date);
exit 0;

#############
# subroutines
#############
sub parse_certificate {
    my ($text) = @_;

    my %result;

    foreach my $line (split(/[\r\n]+/, $text)) {
        if ($line =~ /^\s+Not Before: (.*)$/ ){
            $result{'not_before'} = str2time($1);
        } elsif ($line =~ /^\s+Not After : (.*)$/ ){
            $result{'not_after'} = str2time($1);
        } elsif ($line =~ /^\s+Subject:.*, CN=(.*)$/ ){
            $result{'cn'} = $1;
        }
    }

    if (scalar(keys(%result)) == 0) {
        print("CRITICAL: failed to retrieve certificate\n");
        exit 2; 
    }

    return(\%result);
}

##############################################################################
# Documentation
##############################################################################

=for stopwords
localpath SSL

=head1 NAME 

check_ssl_local - check local SSL certs for expiration

=head1 SYNOPSIS

  check_ssl_local -w DAYS -c DAYS
  check_ssl_local --help

=head1 DESCRIPTION

Checks a local SSL cert file for expiration.

=head1 OPTIONS

=over 4

=item B<--critical|-c> I<DAYS>

Required option - critical alert if expiring in less than DAYS.

=item B<--filename|-f> I<localpath>

Required option - path to local cert file to check.

=item B<--help|-h>

Display the usage for this script. 

=item B<--warning|-w> I<DAYS>

Required option - warning alert if expiring in less than DAYS.

=back

=cut

