|
1 #!/usr/bin/perl |
|
2 |
|
3 use strict; |
|
4 use warnings; |
|
5 use File::Find; |
|
6 use File::Basename; |
|
7 use Fcntl; |
|
8 use DB_File; |
|
9 use Getopt::Long; |
|
10 use IPC::Open2; |
|
11 use Date::Manip; |
|
12 use POSIX ":sys_wait_h"; |
|
13 use lib "/usr/lib/nagios/plugins"; |
|
14 use utils qw (%ERRORS &print_revision &support); |
|
15 |
|
16 my $ME = basename $0; |
|
17 my $VERSION = "0.4"; |
|
18 my $hash_file = "/var/tmp/" . basename($0) . ".known.db"; |
|
19 my %known; |
|
20 my %certs = (); |
|
21 my $no_print = |
|
22 "no_header,no_version,no_serial,no_validity,no_subject,no_issuer,no_pubkey,no_sigdump,no_extensions"; |
|
23 my @cmd_x509 = ( |
|
24 "openssl", "x509", "-noout", "-text", |
|
25 "-certopt", "$no_print", "-subject", "-enddate" |
|
26 ); |
|
27 my @cmd_pkcs12 = |
|
28 ("openssl", "pkcs12", "-clcerts", "-nokeys", "-nomacver", "-passin", "pass:"); |
|
29 my @cmd_pipe = ( |
|
30 "openssl", "x509", "-noout", "-text", |
|
31 "-certopt", $no_print, "-subject", "-enddate" |
|
32 ); |
|
33 |
|
34 my ( |
|
35 $opt_debug, $opt_init, $opt_signature, |
|
36 $opt_version, $opt_help, $opt_warning, $opt_directory, |
|
37 $opt_critical, $opt_binary, @opt_exclude |
|
38 ); |
|
39 my ($file, $w_time, $c_time); |
|
40 |
|
41 my (@critical, @warning); |
|
42 |
|
43 $opt_binary = "/usr/bin/openssl"; |
|
44 $opt_signature = "md5WithRSAEncryption"; |
|
45 $opt_warning = "1month"; |
|
46 $opt_critical = "1week"; |
|
47 $opt_directory = "/etc"; |
|
48 |
|
49 sub process_file(); |
|
50 sub print_help(); |
|
51 sub print_usage(); |
|
52 |
|
53 MAIN: { |
|
54 |
|
55 Getopt::Long::Configure('bundling'); |
|
56 GetOptions( |
|
57 "i|init" => \$opt_init, |
|
58 "h|help" => \$opt_help, |
|
59 "V|version" => \$opt_version, |
|
60 "b|binary=s" => \$opt_binary, |
|
61 "d|directory=s" => \$opt_directory, |
|
62 "w|warning=s" => \$opt_warning, |
|
63 "c|critical=s" => \$opt_critical, |
|
64 "s|signature" => \$opt_signature, |
|
65 "e|exclude=s" => \@opt_exclude, |
|
66 "d|debug" => \$opt_debug, |
|
67 ); |
|
68 |
|
69 print_revision($ME, $VERSION) and exit $ERRORS{"OK"} if $opt_version; |
|
70 print_help() and exit $ERRORS{"OK"} if $opt_help; |
|
71 |
|
72 tie(%known, DB_File => $hash_file, O_RDWR | O_CREAT, 0600) |
|
73 or die "Couldn't tie hash to file $hash_file: $!; aborting"; |
|
74 |
|
75 # initiate file-data hash |
|
76 %known = () if $opt_init; |
|
77 |
|
78 find({ wanted => \&process_file }, $opt_directory); |
|
79 |
|
80 # calculate the time |
|
81 $w_time = DateCalc("today", "+ $opt_warning"); |
|
82 $c_time = DateCalc("today", "+ $opt_critical"); |
|
83 |
|
84 # check expire date |
|
85 foreach (sort keys %certs) { |
|
86 my $enddate; |
|
87 if (@{ $certs{$_} }[1] =~ /(\w+\s+\d+\s+\d+:\d+:\d+\s+\d+)/) { |
|
88 $enddate = $1; |
|
89 } |
|
90 $enddate = ParseDate($enddate); |
|
91 unless ($enddate) { |
|
92 print "CERT CRITICAL: Can't parse enddate\n"; |
|
93 exit $ERRORS{"CRITICAL"}; |
|
94 } |
|
95 |
|
96 &Date_Cmp($enddate, $w_time) > 0 and push(@{ $certs{$_} }, "OK"), next; |
|
97 &Date_Cmp($enddate, $c_time) > 0 |
|
98 and push(@{ $certs{$_} }, "WARNING"), next; |
|
99 push(@{ $certs{$_} }, "CRITICAL"); |
|
100 } |
|
101 |
|
102 # looking for stats |
|
103 foreach (sort keys %certs) { |
|
104 if (@{ $certs{$_} }[2]) { |
|
105 if (@{ $certs{$_} }[2] eq "$opt_signature") { |
|
106 push(@warning, |
|
107 "file: $_, CN=@{$certs{$_}}[0] Signature Algorithm: @{$certs{$_}}[2]" |
|
108 ); |
|
109 } |
|
110 } |
|
111 |
|
112 if (@{ $certs{$_} }[3] eq "WARNING") { |
|
113 push(@warning, |
|
114 "file: $_, CN=@{$certs{$_}}[0] expires @{$certs{$_}}[1]"); |
|
115 } |
|
116 elsif (@{ $certs{$_} }[3] eq "CRITICAL") { |
|
117 push(@critical, |
|
118 "file: $_, CN=@{$certs{$_}}[0] expires @{$certs{$_}}[1]"); |
|
119 } |
|
120 } |
|
121 |
|
122 # return the state |
|
123 if (@critical) { |
|
124 print "CERT CRITICAL: " . join("\n", @critical) . "\n"; |
|
125 exit $ERRORS{"CRITICAL"}; |
|
126 } |
|
127 elsif (@warning) { |
|
128 print "CERT WARNING: @warning\n"; |
|
129 exit $ERRORS{"WARNING"}; |
|
130 } |
|
131 else { |
|
132 print "CERT OK: all certificates in limit\n"; |
|
133 exit $ERRORS{"OK"}; |
|
134 } |
|
135 |
|
136 untie %known; |
|
137 |
|
138 exit; |
|
139 } |
|
140 |
|
141 sub process_file() { |
|
142 return if not -f; |
|
143 |
|
144 my $id = join " ", (stat)[7, 9]; |
|
145 my $is_certificate = 0; |
|
146 my $in_cert = 0; |
|
147 my @cert = (); |
|
148 my ($rc, $temp, $signature, $subject, $enddate); |
|
149 |
|
150 # excluded files |
|
151 @opt_exclude = split(/,/, join(',', @opt_exclude)) if @opt_exclude; |
|
152 foreach my $exclude_file (@opt_exclude) { |
|
153 if ($exclude_file eq $File::Find::name) { |
|
154 $known{$File::Find::name} = $id; |
|
155 return; |
|
156 } |
|
157 } |
|
158 |
|
159 return |
|
160 if exists $known{$File::Find::name} |
|
161 and $known{$File::Find::name} eq $id; |
|
162 |
|
163 # checking for pkcs12 certificates |
|
164 my @cmd_pkcs12_current = @cmd_pkcs12; |
|
165 push @cmd_pkcs12_current, "-in", $File::Find::name, "2>/dev/null"; |
|
166 |
|
167 my $cid = open(FILE, "@cmd_pkcs12_current |") || die "Can't fork: $!"; |
|
168 |
|
169 while (<FILE>) { |
|
170 /^$cid:error:.*/ and last; |
|
171 $temp .= $_; |
|
172 } |
|
173 close(FILE); |
|
174 |
|
175 if ($temp) { |
|
176 local (*READ, *WRITE); |
|
177 my $cid = open2(\*READ, \*WRITE, @cmd_pipe) |
|
178 or die "Can't fork: $!\n"; |
|
179 print WRITE $temp; |
|
180 close(WRITE); |
|
181 while (<READ>) { |
|
182 /Signature\sAlgorithm:\s(.*)\s+$/ and $signature = $1; |
|
183 /^subject=\s+(.*)\s+$/ and $subject = $1; |
|
184 /^notAfter=(.*)\s+$/ and $enddate = $1; |
|
185 } |
|
186 close(READ); |
|
187 |
|
188 # waiting for child processes |
|
189 do { |
|
190 $cid = waitpid(-1, WNOHANG); |
|
191 } while $cid > 0; |
|
192 |
|
193 $is_certificate = 1; |
|
194 push(@{ $certs{$File::Find::name} }, ($subject, $enddate, $signature)); |
|
195 |
|
196 $known{$File::Find::name} = $id if not($is_certificate); |
|
197 return; |
|
198 } |
|
199 |
|
200 open(FILE, $File::Find::name) or die "can't open $_: $!"; |
|
201 |
|
202 while (<FILE>) { |
|
203 |
|
204 # cheking for x509 certificates |
|
205 if ($in_cert) { |
|
206 push @cert, $_; |
|
207 if (/^-----END CERTIFICATE-----$/) { |
|
208 $in_cert = 0; |
|
209 |
|
210 # open filehandles (for read and write) |
|
211 local (*READ, *WRITE); |
|
212 my $cid = open2(\*READ, \*WRITE, @cmd_x509) |
|
213 or die "Can' fork: $!\n"; |
|
214 print WRITE @cert; |
|
215 close(WRITE); |
|
216 @cert = (); |
|
217 |
|
218 while (<READ>) { |
|
219 /Signature\sAlgorithm:\s(.*)\s+$/ and $signature = $1; |
|
220 /^subject=\s+(.*)$/ and $subject = $1; |
|
221 /^notAfter=(.*)\s+$/ and $enddate = $1; |
|
222 } |
|
223 close(READ); |
|
224 |
|
225 # waiting for child processes |
|
226 do { |
|
227 $cid = waitpid(-1, WNOHANG); |
|
228 } while $cid > 0; |
|
229 |
|
230 if ($opt_debug) { |
|
231 print "-----\n"; |
|
232 print "$File::Find::name\n"; |
|
233 print "Signature Algorithm: $signature\n" if ($signature); |
|
234 print "subject: $subject\n" if ($subject); |
|
235 print "enddate: $enddate\n" if ($enddate); |
|
236 } |
|
237 |
|
238 push( |
|
239 @{ $certs{$File::Find::name} }, |
|
240 ($subject, $enddate, $signature) |
|
241 ); |
|
242 $is_certificate = 1; |
|
243 next; |
|
244 } |
|
245 } |
|
246 |
|
247 if (/^-----BEGIN CERTIFICATE-----$/) { |
|
248 $in_cert = 1; |
|
249 push @cert, $_; |
|
250 next; |
|
251 } |
|
252 } |
|
253 |
|
254 $known{$File::Find::name} = $id if not($is_certificate); |
|
255 close(FILE); |
|
256 } |
|
257 |
|
258 sub print_usage() { |
|
259 print "Usage:\n"; |
|
260 print |
|
261 " $ME [-b <binary>] [-i init] [-d <path>] [-w <time>] [-c <time>] [-s <signature algorithm>] [-e <file,file,file,...>]\n"; |
|
262 print " $ME [-h | --help]\n"; |
|
263 print " $ME [-V | --version]\n"; |
|
264 } |
|
265 |
|
266 sub print_help() { |
|
267 print_revision($ME, $VERSION); |
|
268 print "Copyright (c) 2009 Christian Arnold\n\n"; |
|
269 print "This plugin checks the expire date for openssl certificates.\n\n"; |
|
270 print_usage(); |
|
271 print "\n"; |
|
272 print " -b, --binary <binary>\n"; |
|
273 print " Path of openssl binary (default: /usr/bin/openssl)\n"; |
|
274 print " -d, --directory <path>\n"; |
|
275 print " Absolute directory path in which will be recursively search for certificate files (default: /etc).\n"; |
|
276 print " -w, --warning <time>\n"; |
|
277 print |
|
278 " Certificat should not be more than this time older (default: 1month).\n"; |
|
279 print |
|
280 " For time can be used year, month, day, hour, minute, second and weeks.\n"; |
|
281 print " -c, --critical <time>\n"; |
|
282 print |
|
283 " Certificat should not be more than this time older (default: 1week)\n"; |
|
284 print |
|
285 " For time can be used year, month, day, hour, minute, second and weeks.\n"; |
|
286 print " -s, --signature <signature algorithm>\n"; |
|
287 print |
|
288 " Return WARNING status if <signature algorithm> is used (default: md5WithRSAEncryption).\n"; |
|
289 print " -e, --exclude <file,file,file, ...>\n"; |
|
290 print |
|
291 " Absolute path of excluded files, use comma-separated lists for multiple files.\n"; |
|
292 print " -h, --help\n"; |
|
293 print " Print detailed help screen\n"; |
|
294 print " -V, --version\n"; |
|
295 print " Print version information\n"; |
|
296 print "\n"; |
|
297 support(); |
|
298 } |