+#!/usr/bin/perl -w
+# Check peer certificate validity
+# Require perl module : IO::Socket, Net::SSLeay, Date::Parse
+# Require unix programs : openssl, echo, sendmail
+#
+# Copyright (C) 2003 Emmanuel Lacour <elacour@home-dn.net>
+#
+# This file 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 2, or (at your option) any
+# later version.
+#
+# This file 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 file; see the file COPYING. If not, write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+
+
+# Local variables are prefixed with "l_"
+
+use strict;
+use IO::Socket;
+use Net::SSLeay;
+use Getopt::Long;
+use Date::Parse;
+
+Net::SSLeay::SSLeay_add_ssl_algorithms();
+Net::SSLeay::randomize();
+
+my $VERSION = '0.6.2';
+my $AUTHOR = 'Emmanuel Lacour, <elacour@home-dn.net>';
+# Default values
+my $opensslpath = "/usr/bin/openssl";
+my $sendmailpath = "/usr/lib/sendmail";
+my $mailreport = 0;
+my $alert = 5;
+my $mail = "root";
+my $conf = "/etc/sslexpire/sslexpire.conf";
+
+my %hosts;
+my $host;
+my @ports;
+my $port;
+my $portlist;
+my $rhost;
+my $rport;
+my $tmp;
+
+
+
+# Get options
+# output : -m = mail, default = stdout
+# host : -h host, -p port, default = from config file
+# config : -f configfile, default /etc/sslexpire/sslexpire.conf
+# standard : --help, --version
+
+my %opts;
+GetOptions (\%opts,
+ 'host|h=s',
+ 'port|p=s',
+ 'mail|m',
+ 'conf|c=s',
+ 'verbose|v',
+ 'help',
+ 'version|');
+
+if (($opts{'host'}) && ($opts{'port'})) {
+ push @{$hosts{$opts{'host'}}}, $opts{'port'};
+}
+
+$mailreport = 1 if ($opts{'mail'});
+
+if ($opts{'conf'}) {
+ $conf = $opts{'conf'};
+}
+
+if ($opts{'help'}) {
+ &usage;
+}
+
+if ($opts{'version'}) {
+ print "sslexpire $VERSION\n";
+ print "Written by $AUTHOR\n";
+ exit;
+}
+
+# Parse config file
+
+
+if (-f $conf) {
+ open (CONF,$conf);
+
+ while (<CONF>) {
+ # Skip comments
+ next if (/^[ ]*#/);
+ # Alert param.
+ if (/^alert[ ]*=/) {
+ ($tmp,$alert) = split /=/, $_;
+ # Mail param.
+ } elsif (/^mail[ ]*=/) {
+ ($tmp,$mail) = split /=/, $_;
+ # Use hosts from config file if noone are given by command line
+ } elsif ((!$opts{'host'}) && (!$opts{'port'}) && (/:/)) {
+ ($tmp,$portlist) = split /:/, $_;
+ chomp ($tmp);
+ chomp ($portlist);
+ # There is multiple ports
+ if (/,/) {
+ @ports = split /,/, $portlist;
+ foreach (@ports) {
+ push @{$hosts{$tmp}}, $_;
+ }
+ # There is only one port
+ } else {
+ push @{$hosts{$tmp}},$portlist;
+ }
+ }
+
+ }
+
+ close CONF;
+}
+
+# Print program usage
+sub usage {
+ print "Usage: sslexpire [OPTION]...
+-h, --host=HOST check only this host
+-p, --port=TCPPORT check this port on the previous host
+-m, --mail report by mail instead of STDOUT
+-c, --conf=FILE use this config file
+ --help print this help, then exit
+";
+ exit;
+}
+
+
+# This will return the expiration date
+sub getExpire {
+
+ my ($l_host,$l_port) = @_;
+ my ($l_expdate,$l_comment);
+
+ # Connect to $l_host:$l_port
+ my $socket = IO::Socket::INET->new(
+ Proto => "tcp",
+ PeerAddr => $l_host,
+ PeerPort => $l_port
+ );
+ # If we connected successfully
+ if ($socket) {
+ # Intiate ssl
+ my $l_ctx = Net::SSLeay::CTX_new();
+ my $l_ssl = Net::SSLeay::new($l_ctx);
+
+ Net::SSLeay::set_fd($l_ssl, fileno($socket));
+ my $res = Net::SSLeay::connect($l_ssl);
+
+ # Get peer certificate
+ my $l_x509 = Net::SSLeay::get_peer_certificate($l_ssl);
+ if ($l_x509) {
+ my $l_string = Net::SSLeay::PEM_get_string_X509($l_x509);
+ # Get the expiration date, using openssl
+ ($l_expdate,$l_comment) = split(/\n/, `echo "$l_string" | $opensslpath x509 -enddate -subject -noout 2>&1`);
+ $l_expdate =~ s/.*=//;
+ chomp($l_expdate);
+ } else {
+ $l_expdate = 1;
+ $l_comment = 1;
+ }
+
+ # Close and cleanup
+ Net::SSLeay::free($l_ssl);
+ Net::SSLeay::CTX_free($l_ctx);
+ close $socket;
+ } else {
+ $l_expdate = 1;
+ $l_comment = 1;
+ }
+ return ($l_expdate,$l_comment);
+}
+
+
+# Report if needed
+#
+#
+sub report {
+ # Convert date into epoch using date command
+ my ($l_expdate,$l_comment,$l_host,$l_port) = @_;
+ my $l_subject = "";
+
+ if ($l_expdate ne "1") {
+ # The current date
+ my $l_today = time;
+ my $l_epochdate = str2time($l_expdate);
+
+ # Calculate diff between expiration date and today
+ my $l_diff = ($l_epochdate - $l_today)/(3600*24);
+
+ # Report if needed
+ if ($l_diff < $alert) {
+ $l_subject = "Warning ssl certificate on $l_host:$l_port expires in $l_diff days:" if ($l_diff > 1);
+ $l_subject = "Warning ssl certificate on $l_host:$l_port expires today:" if (($l_diff > 0) && ($l_diff < 1));
+ $l_subject = "Warning ssl certificate on $l_host:$l_port expired:" if ($l_diff <= 0);
+ my $l_mesg = "Expiration date: $l_expdate\n$l_comment\n";
+ # Mail report
+ if ($mailreport) {
+ sendmail($mail, $l_subject, $l_mesg);
+ } else {
+ print "$l_subject\n";
+ print "$l_mesg\n";
+ }
+ }
+ } else {
+ $l_subject = "Unable to read certificate on $l_host:$l_port!";
+ if ($mailreport) {
+ sendmail($mail, $l_subject, "");
+ } else {
+ print "$l_subject\n";
+ }
+ }
+}
+
+
+# Send mail - sendmail (to,subject,body)
+sub sendmail {
+ my $to = shift;
+ my $subj = shift;
+ my $mesg = shift;
+ chomp ($to);
+ chomp ($subj);
+ chomp ($mesg);
+ open (MAIL,"| $sendmailpath -t") or die "Couldn't open $sendmailpath";
+ print MAIL "To: $to\n";
+ print MAIL "Subject: $subj\n";
+ print MAIL "\n";
+ print MAIL "$mesg\n" if $mesg;
+ close MAIL;
+ if ((my $status = $?>>8) != 0) {
+ die "sendmail: exit status $status\n";
+ }
+}
+
+# Main
+#
+
+
+# We haven't hosts to check...
+if (!%hosts) {&usage;};
+
+
+# Parse hosts
+foreach $host (keys %hosts) {
+ # Parse ports for each hosts
+ foreach $port (@{$hosts{$host}}) {
+ if ($opts{'verbose'}) {
+ print "Checking\t$host:$port\n";
+ }
+
+ # Get expiration date
+ my ($expdate,$comment) = &getExpire($host,$port);
+
+ # Report
+ &report("$expdate","$comment","$host","$port");
+ }
+}
+
+
+# script documentation (POD style)
+
+=head1 NAME
+
+sslexpire - Remotely check ssl certificate expiration date.
+
+=head1 DESCRIPTION
+
+This program connect to an host:port to retrieve the expiration date of the ssl
+certificate. It gives a report to STDOUT or by email using configuration file.
+
+=head1 COMMAND LINE PARAMETERS
+
+Optional command line parameters are the host and the port to connect. This
+allow checking a single host instead of using those given in the configuration
+file for periodically checks.
+
+=head1 OPTIONS
+
+=head2 B<-c> I<FILE>, B<--conf>=I<FILE>
+
+Specify an alternate config file.
+
+=head2 B<-h> I<HOST>, B<--host>=I<HOST>
+
+Connect to I<HOST> instead of those given in the config file.
+
+=head2 B<-p> I<PORT>, B<--port>=I<PORT>
+
+Specify the port to connect to (used in conjonction with --host).
+
+=head2 B<-v>, B<--verbose>
+
+Prints out verbose messages.
+
+=head2 B<-m>, B<--mail>
+
+Send report by mail instead of STDOUT. It will use the address given in the
+config file or root by default.
+
+=head2 B<--help>
+
+Prints out command-line help.
+
+=head2 B<--version>
+
+Prints out version information.
+
+=head1 FILES
+
+/etc/sslexpire/sslexpire.conf
+
+=head1 AUTHOR
+
+Emmanuel Lacour, elacour@home-dn.net
+
+=cut