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