610badfdc6b2e971c96e46cb7610cf64e108c005
[manu/sslexpire.git] / sslexpire / sslexpire
1 #!/usr/bin/perl -w
2 # Check peer certificate validity
3 # Require perl module : IO::Socket, Net::SSLeay, Date::Parse
4 # Require unix programs : openssl, echo, sendmail
5 #
6 # Copyright (C) 2003 Emmanuel Lacour <elacour@home-dn.net>
7 #
8 # This file is free software; you can redistribute it and/or modify it
9 # under the terms of the GNU General Public License as published by the
10 # Free Software Foundation; either version 2, or (at your option) any
11 # later version.
12 #
13 # This file is distributed in the hope that it will be
14 # useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 # General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this file; see the file COPYING.  If not, write to the Free
20 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 # 02110-1301, USA.
22 #
23
24
25 # Local variables are prefixed with "l_"
26
27 use strict;
28 use IO::Socket;
29 use Net::SSLeay;
30 use Getopt::Long;
31 use Date::Parse;
32
33 Net::SSLeay::SSLeay_add_ssl_algorithms();
34 Net::SSLeay::randomize();
35
36 my $VERSION = '0.6.2';
37 my $AUTHOR = 'Emmanuel Lacour, <elacour@home-dn.net>';
38 # Default values
39 my $opensslpath = "/usr/bin/openssl";
40 my $sendmailpath = "/usr/lib/sendmail";
41 my $mailreport = 0;
42 my $alert = 5;
43 my $mail = "root";
44 my $conf = "/etc/sslexpire/sslexpire.conf";
45
46 my %hosts;
47 my $host;
48 my @ports;
49 my $port;
50 my $portlist;
51 my $rhost;
52 my $rport;
53 my $tmp;
54
55
56
57 # Get options
58 # output : -m = mail, default = stdout
59 # host : -h host, -p port, default = from config file
60 # config : -f configfile, default /etc/sslexpire/sslexpire.conf 
61 # standard : --help, --version
62
63 my %opts;
64 GetOptions (\%opts,
65     'host|h=s',
66     'port|p=s',
67     'mail|m',
68     'conf|c=s',
69     'verbose|v',
70         'help',
71         'version|');
72
73 if (($opts{'host'}) && ($opts{'port'})) {
74     push @{$hosts{$opts{'host'}}}, $opts{'port'};
75 }
76
77 $mailreport = 1 if ($opts{'mail'});
78
79 if ($opts{'conf'}) {
80     $conf = $opts{'conf'};
81 }
82
83 if ($opts{'help'}) {
84     &usage;
85 }
86
87 if ($opts{'version'}) {
88     print "sslexpire $VERSION\n";
89     print "Written by $AUTHOR\n";
90     exit;
91 }
92
93 # Parse config file
94
95
96 if (-f $conf) {
97     open (CONF,$conf);
98     
99     while (<CONF>) {
100         # Skip comments
101         next if (/^[    ]*#/);
102         # Alert param.
103         if (/^alert[    ]*=/) { 
104             ($tmp,$alert) = split /=/, $_;
105         # Mail param.
106         } elsif (/^mail[        ]*=/) { 
107             ($tmp,$mail) = split /=/, $_;
108         # Use hosts from config file if noone are given by command line
109         } elsif ((!$opts{'host'}) && (!$opts{'port'}) && (/:/)) {
110             ($tmp,$portlist) = split /:/, $_;
111             chomp ($tmp);
112             chomp ($portlist);
113             # There is multiple ports
114             if (/,/) {
115                 @ports = split /,/, $portlist;
116                 foreach (@ports) {
117                     push @{$hosts{$tmp}}, $_;
118                 }
119             # There is only one port
120             } else {
121                push @{$hosts{$tmp}},$portlist;
122             }
123         }
124             
125     }
126     
127     close CONF;
128 }
129
130 # Print program usage
131 sub usage {
132     print "Usage: sslexpire [OPTION]...
133 -h, --host=HOST        check only this host
134 -p, --port=TCPPORT     check this port on the previous host
135 -m, --mail             report by mail instead of STDOUT
136 -c, --conf=FILE        use this config file
137     --help             print this help, then exit
138 ";
139     exit;
140 }
141
142
143 # This will return the expiration date
144 sub getExpire {
145
146     my ($l_host,$l_port) = @_;
147     my ($l_expdate,$l_comment);
148
149     # Connect to $l_host:$l_port
150     my $socket = IO::Socket::INET->new(
151         Proto => "tcp",
152         PeerAddr => $l_host,
153         PeerPort => $l_port
154         );
155     # If we connected successfully
156     if ($socket) {
157         # Intiate ssl
158         my $l_ctx = Net::SSLeay::CTX_new();
159         my $l_ssl = Net::SSLeay::new($l_ctx);
160
161         Net::SSLeay::set_fd($l_ssl, fileno($socket));
162         my $res = Net::SSLeay::connect($l_ssl);
163
164         # Get peer certificate
165         my $l_x509 = Net::SSLeay::get_peer_certificate($l_ssl);
166         if ($l_x509) {
167             my $l_string = Net::SSLeay::PEM_get_string_X509($l_x509);
168             # Get the expiration date, using openssl
169             ($l_expdate,$l_comment) = split(/\n/, `echo "$l_string" | $opensslpath x509 -enddate -subject -noout 2>&1`);
170             $l_expdate =~ s/.*=//;
171             chomp($l_expdate);
172         } else {
173             $l_expdate = 1;
174             $l_comment = 1;
175         }
176
177         # Close and cleanup
178         Net::SSLeay::free($l_ssl);
179         Net::SSLeay::CTX_free($l_ctx);
180         close $socket;
181     } else {
182             $l_expdate = 1;
183             $l_comment = 1;
184     }
185     return ($l_expdate,$l_comment);
186 }
187
188
189 # Report if needed
190
191 #
192 sub report {
193     # Convert date into epoch using date command
194     my ($l_expdate,$l_comment,$l_host,$l_port) = @_;
195     my $l_subject = "";
196     
197     if ($l_expdate ne "1") {
198         # The current date
199         my $l_today =  time;
200         my $l_epochdate = str2time($l_expdate);
201     
202         # Calculate diff between expiration date and today
203         my $l_diff = ($l_epochdate - $l_today)/(3600*24);
204
205         # Report if needed
206         if ($l_diff < $alert) {
207             $l_subject = "Warning ssl certificate on $l_host:$l_port expires in $l_diff days:" if ($l_diff > 1);
208             $l_subject = "Warning ssl certificate on $l_host:$l_port expires today:" if (($l_diff > 0) && ($l_diff < 1));
209             $l_subject = "Warning ssl certificate on $l_host:$l_port expired:" if ($l_diff <= 0);
210             my $l_mesg = "Expiration date: $l_expdate\n$l_comment\n";
211             # Mail report
212             if ($mailreport) {
213                 sendmail($mail, $l_subject, $l_mesg);
214             } else {
215                 print "$l_subject\n";
216                 print "$l_mesg\n";
217             }
218         }
219     } else {
220         $l_subject = "Unable to read certificate on $l_host:$l_port!";
221             if ($mailreport) {
222                 sendmail($mail, $l_subject, "");
223             } else {
224                 print "$l_subject\n";
225             }
226     }
227 }
228
229
230 # Send mail - sendmail (to,subject,body)
231 sub sendmail {
232     my $to = shift;
233     my $subj = shift;
234     my $mesg = shift;
235     chomp ($to);
236     chomp ($subj);
237     chomp ($mesg);
238     open (MAIL,"| $sendmailpath -t") or die "Couldn't open $sendmailpath";
239     print MAIL "To: $to\n";
240     print MAIL "Subject: $subj\n";
241     print MAIL "\n";
242     print MAIL "$mesg\n" if $mesg;
243     close MAIL;
244     if ((my $status = $?>>8) != 0) {
245        die "sendmail: exit status $status\n";
246     }
247 }
248
249 # Main
250 #
251
252
253 # We haven't hosts to check...
254 if (!%hosts) {&usage;};
255
256
257 # Parse hosts
258 foreach $host (keys %hosts) {
259     # Parse ports for each hosts
260     foreach $port (@{$hosts{$host}}) {
261         if ($opts{'verbose'}) {
262             print "Checking\t$host:$port\n";
263         }
264     
265         # Get expiration date
266         my ($expdate,$comment) = &getExpire($host,$port);
267     
268         # Report
269         &report("$expdate","$comment","$host","$port");
270     }
271 }
272
273
274 # script documentation (POD style)
275
276 =head1 NAME
277
278 sslexpire - Remotely check ssl certificate expiration date.
279
280 =head1 DESCRIPTION
281
282 This program connect to an host:port to retrieve the expiration date of the ssl
283 certificate. It gives a report to STDOUT or by email using configuration file.
284
285 =head1 COMMAND LINE PARAMETERS
286
287 Optional command line parameters are the host and the port to connect. This
288 allow checking a single host instead of using those given in the configuration
289 file for periodically checks.
290
291 =head1 OPTIONS
292
293 =head2 B<-c> I<FILE>, B<--conf>=I<FILE>
294     
295 Specify an alternate config file.
296
297 =head2 B<-h> I<HOST>, B<--host>=I<HOST>
298
299 Connect to I<HOST> instead of those given in the config file.
300
301 =head2 B<-p> I<PORT>, B<--port>=I<PORT>
302
303 Specify the port to connect to (used in conjonction with --host).
304
305 =head2 B<-v>, B<--verbose>
306
307 Prints out verbose messages.
308
309 =head2 B<-m>, B<--mail>
310
311 Send report by mail instead of STDOUT. It will use the address given in the
312 config file or root by default.
313
314 =head2 B<--help>
315
316 Prints out command-line help.
317
318 =head2 B<--version>
319
320 Prints out version information.
321
322 =head1 FILES
323         
324 /etc/sslexpire/sslexpire.conf
325
326 =head1 AUTHOR
327
328 Emmanuel Lacour, elacour@home-dn.net
329
330 =cut