12 use POSIX ":sys_wait_h"; |
12 use POSIX ":sys_wait_h"; |
13 use feature qw(:5.10); |
13 use feature qw(:5.10); |
14 use lib "/usr/lib/nagios/plugins"; |
14 use lib "/usr/lib/nagios/plugins"; |
15 use utils qw (%ERRORS &print_revision &support); |
15 use utils qw (%ERRORS &print_revision &support); |
16 |
16 |
17 my $ME = basename $0; |
17 my $ME = basename $0; |
18 my $USAGE = <<EOF; |
18 my $USAGE = <<EOF; |
19 Usage: $ME [-b <binary>] [-i init] [-d <path>] [-w <time>] [-c <time>] [-s <signature algorithm>] [-e <file,file,file,...>] |
19 Usage: $ME [-b <binary>] [-i init] [-d <path>] [-w <time>] [-c <time>] [-s <signature algorithm>] [-e <file,file,file,...>] |
20 $ME [-h | --help] |
20 $ME [-h | --help] |
21 $ME [-V | --version] |
21 $ME [-V | --version] |
|
22 |
|
23 Consult „--help“ for even more options. |
22 EOF |
24 EOF |
23 my $VERSION = "0.4"; |
25 my $VERSION = "0.4"; |
24 my $hash_file = "/var/tmp/" . basename($0) . ".known.db"; |
26 |
25 my (%known, %excluded); |
|
26 my %certs = (); |
|
27 my $no_print = |
27 my $no_print = |
28 "no_header,no_version,no_serial,no_validity,no_subject,no_issuer,no_pubkey,no_sigdump,no_extensions"; |
28 "no_header,no_version,no_serial,no_validity,no_subject,no_issuer,no_pubkey,no_sigdump,no_extensions"; |
29 my @cmd_x509 = ( |
29 my @cmd_x509 = ( |
30 "openssl", "x509", "-noout", "-text", |
30 "openssl", "x509", "-noout", "-text", |
31 "-certopt", "$no_print", "-subject", "-enddate" |
31 "-certopt", "$no_print", "-subject", "-enddate" |
32 ); |
32 ); |
33 my @cmd_pkcs12 = qw(openssl pkcs12 -clcerts -nokeys -nomacver -passin pass:); |
33 my @cmd_pkcs12 = qw(openssl pkcs12 -clcerts -nokeys -nomacver -passin pass:); |
34 my @cmd_pipe = ( |
34 my @cmd_pipe = ( |
35 "openssl", "x509", "-noout", "-text", |
35 "openssl", "x509", "-noout", "-text", |
36 "-certopt", $no_print, "-subject", "-enddate" |
36 "-certopt", $no_print, "-subject", "-enddate" |
37 ); |
37 ); |
38 |
38 |
39 my ( |
39 my (%cache, %excluded, %certs); |
40 $opt_debug, $opt_init, $opt_signature, $opt_version, |
40 |
41 $opt_help, $opt_warning, $opt_directory, $opt_critical, |
41 my $opt_binary = "/usr/bin/openssl"; |
42 $opt_binary, @opt_exclude |
42 my $opt_signature = "md5WithRSAEncryption"; |
43 ); |
43 my $opt_warning = "1month"; |
44 my ($file, $w_time, $c_time); |
44 my $opt_critical = "1week"; |
45 |
45 my $opt_directory = "/etc"; |
46 my (@critical, @warning); |
46 my $opt_debug = 0; |
47 |
47 my $opt_init = undef; |
48 $opt_binary = "/usr/bin/openssl"; |
48 my @opt_exclude = (); |
49 $opt_signature = "md5WithRSAEncryption"; |
49 |
50 $opt_warning = "1month"; |
50 my $opt_cache = "/var/tmp/@{[basename($0)]}.known.db"; |
51 $opt_critical = "1week"; |
|
52 $opt_directory = "/etc"; |
|
53 |
51 |
54 sub process_file(); |
52 sub process_file(); |
55 sub print_help(); |
53 sub print_help(); |
56 sub print_usage(); |
54 sub print_usage(); |
57 sub get_id(;$); |
55 sub get_id(;$); |
58 |
56 |
59 MAIN: { |
57 MAIN: { |
|
58 my ($w_time, $c_time); |
|
59 my (@critical, @warning); |
60 |
60 |
61 Getopt::Long::Configure('bundling'); |
61 Getopt::Long::Configure('bundling'); |
62 GetOptions( |
62 GetOptions( |
63 "i|init" => \$opt_init, |
63 "i|init" => \$opt_init, |
64 "h|help" => \$opt_help, |
64 "h|help" => sub { print_help(); exit $ERRORS{OK}; }, |
65 "V|version" => \$opt_version, |
65 "V|version" => sub { print_revision($ME, $VERSION); exit $ERRORS{OK}; }, |
66 "b|binary=s" => \$opt_binary, |
66 "b|binary=s" => \$opt_binary, |
67 "d|directory=s" => \$opt_directory, |
67 "d|directory=s" => \$opt_directory, |
68 "w|warning=s" => \$opt_warning, |
68 "w|warning=s" => \$opt_warning, |
69 "c|critical=s" => \$opt_critical, |
69 "c|critical=s" => \$opt_critical, |
70 "s|signature" => \$opt_signature, |
70 "s|signature" => \$opt_signature, |
71 "e|exclude=s" => \@opt_exclude, |
71 "e|exclude=s" => \@opt_exclude, |
72 "d|debug" => \$opt_debug, |
72 "d|debug" => \$opt_debug, |
|
73 "cache=s" => \$opt_cache, |
73 ); |
74 ); |
74 |
75 |
75 print_revision($ME, $VERSION) and exit $ERRORS{"OK"} if $opt_version; |
76 tie(%cache, DB_File => $opt_cache, O_RDWR | O_CREAT, 0600) |
76 print_help() and exit $ERRORS{"OK"} if $opt_help; |
77 or die "Couldn't tie hash to file $opt_cache: $!; aborting"; |
77 |
|
78 tie(%known, DB_File => $hash_file, O_RDWR | O_CREAT, 0600) |
|
79 or die "Couldn't tie hash to file $hash_file: $!; aborting"; |
|
80 |
78 |
81 # initiate file-data hash |
79 # initiate file-data hash |
82 %known = () if $opt_init; |
80 %cache = () if $opt_init; |
83 @excluded{@opt_exclude} = (); |
81 @excluded{@opt_exclude} = (); |
84 |
82 |
85 find({ wanted => \&process_file }, $opt_directory); |
83 find({ wanted => \&process_file }, $opt_directory); |
86 |
84 |
87 exit; |
85 exit; |
88 |
86 |
89 # calculate the time |
87 # calculate the time |
90 # $w_time = time() + 4 * 7 * 86400; |
88 # $w_time = time() + 4 * 7 * 86400; |
91 $w_time = DateCalc("today", "+ $opt_warning"); |
89 $w_time = DateCalc("today", "+ $opt_warning"); |
92 $c_time = DateCalc("today", "+ $opt_critical"); |
90 $c_time = DateCalc("today", "+ $opt_critical"); |
93 |
91 |
94 # check expire date |
92 # check expire date |
95 foreach (sort keys %certs) { |
93 foreach (sort keys %certs) { |
141 else { |
139 else { |
142 print "CERT OK: all certificates in limit\n"; |
140 print "CERT OK: all certificates in limit\n"; |
143 exit $ERRORS{"OK"}; |
141 exit $ERRORS{"OK"}; |
144 } |
142 } |
145 |
143 |
146 untie %known; |
144 untie %cache; |
147 |
145 |
148 exit; |
146 exit; |
149 } |
147 } |
150 |
148 |
151 sub get_id(;$) { return join " ", (@_ ? stat($_[0]) : stat(_))[7, 9] } |
149 sub get_id(;$) { return join " ", (@_ ? stat($_[0]) : stat(_))[7, 9] } |
152 |
150 |
153 sub process_file() { |
151 sub process_file() { |
154 |
152 |
155 return if exists $excluded{$File::Find::name}; |
153 return if exists $excluded{$File::Find::name}; |
156 return if not -f; |
154 return if not -f; |
157 |
155 |
158 my $id = get_id(); |
156 my $id = get_id(); |
159 my $is_certificate = 0; |
157 my $is_certificate = 0; |
160 my $in_cert = 0; |
158 my $in_cert = 0; |
161 my @cert = (); |
159 my @cert = (); |
162 my ($rc, $temp, $signature, $subject, $enddate); |
160 my ($rc, $temp, $signature, $subject, $enddate); |
163 |
161 |
164 # excluded files |
162 # excluded files |
165 # @opt_exclude = split(/,/, join(',', @opt_exclude)) if @opt_exclude; |
163 # @opt_exclude = split(/,/, join(',', @opt_exclude)) if @opt_exclude; |
166 |
164 |
167 return |
165 return |
168 if exists $known{$File::Find::name} |
166 if exists $cache{$File::Find::name} |
169 and $known{$File::Find::name} eq $id; |
167 and $cache{$File::Find::name} eq $id; |
170 |
168 |
171 #say $File::Find::name; |
169 #say $File::Find::name; |
172 # checking for pkcs12 certificates |
170 # checking for pkcs12 certificates |
173 |
171 |
174 if (0) { |
172 if (0) { |
175 my @cmd_pkcs12_current = @cmd_pkcs12; |
173 my @cmd_pkcs12_current = @cmd_pkcs12; |
176 push @cmd_pkcs12_current, "-in", $File::Find::name, "2>/dev/null"; |
174 push @cmd_pkcs12_current, "-in", $File::Find::name, "2>/dev/null"; |
177 my $cid = open(FILE, "@cmd_pkcs12_current |") || die "Can't fork: $!"; |
175 my $cid = open(FILE, "@cmd_pkcs12_current |") || die "Can't fork: $!"; |
178 |
176 |
179 while (<FILE>) { |
177 while (<FILE>) { |
180 /^$cid:error:.*/ and last; |
178 /^$cid:error:.*/ and last; |
181 $temp .= $_; |
179 $temp .= $_; |
182 } |
180 } |
|
181 close(FILE); |
|
182 |
|
183 if ($temp) { |
|
184 local (*READ, *WRITE); |
|
185 my $cid = open2(\*READ, \*WRITE, @cmd_pipe) |
|
186 or die "Can't fork: $!\n"; |
|
187 print WRITE $temp; |
|
188 close(WRITE); |
|
189 while (<READ>) { |
|
190 /Signature\sAlgorithm:\s(.*)\s+$/ and $signature = $1; |
|
191 /^subject=\s+(.*)\s+$/ and $subject = $1; |
|
192 /^notAfter=(.*)\s+$/ and $enddate = $1; |
|
193 } |
|
194 close(READ); |
|
195 |
|
196 # waiting for child processes |
|
197 do { |
|
198 $cid = waitpid(-1, WNOHANG); |
|
199 } while $cid > 0; |
|
200 |
|
201 $is_certificate = 1; |
|
202 push( |
|
203 @{ $certs{$File::Find::name} }, |
|
204 ($subject, $enddate, $signature) |
|
205 ); |
|
206 |
|
207 $cache{$File::Find::name} = $id if not($is_certificate); |
|
208 return; |
|
209 } |
|
210 } |
|
211 |
|
212 open(FILE, $_) or die "can't open $File::Find::name: $!"; |
|
213 my $file = join "", <FILE>; |
183 close(FILE); |
214 close(FILE); |
184 |
215 |
185 if ($temp) { |
216 while ($file =~ /^(-+BEGIN CERTIFICATE.*?-+END CERTIFICATE-+)$/msg) { |
|
217 |
|
218 # open filehandles (for read and write) |
186 local (*READ, *WRITE); |
219 local (*READ, *WRITE); |
187 my $cid = open2(\*READ, \*WRITE, @cmd_pipe) |
220 my $cid = open2(\*READ, \*WRITE, @cmd_x509) |
188 or die "Can't fork: $!\n"; |
221 or die "Can' fork: $!\n"; |
189 print WRITE $temp; |
222 print WRITE $1; |
190 close(WRITE); |
223 close(WRITE); |
|
224 |
191 while (<READ>) { |
225 while (<READ>) { |
192 /Signature\sAlgorithm:\s(.*)\s+$/ and $signature = $1; |
226 /Signature\sAlgorithm:\s(.*)\s+$/ and $signature = $1; |
193 /^subject=\s+(.*)\s+$/ and $subject = $1; |
227 /^subject=\s+(.*)$/ and $subject = $1; |
194 /^notAfter=(.*)\s+$/ and $enddate = $1; |
228 /^notAfter=(.*)\s+$/ and $enddate = $1; |
195 } |
229 } |
196 close(READ); |
230 close(READ); |
197 |
231 |
198 # waiting for child processes |
232 waitpid($cid, 0) < 0 and die "no child with pid $cid\n"; |
199 do { |
233 |
200 $cid = waitpid(-1, WNOHANG); |
234 if ($opt_debug) { |
201 } while $cid > 0; |
235 print "-----\n"; |
|
236 print "$File::Find::name\n"; |
|
237 print "Signature Algorithm: $signature\n" if ($signature); |
|
238 print "subject: $subject\n" if ($subject); |
|
239 print "enddate: $enddate\n" if ($enddate); |
|
240 } |
|
241 |
|
242 push(@{ $certs{$File::Find::name} }, ($subject, $enddate, $signature)); |
202 |
243 |
203 $is_certificate = 1; |
244 $is_certificate = 1; |
204 push(@{ $certs{$File::Find::name} }, ($subject, $enddate, $signature)); |
245 } |
205 |
246 |
206 $known{$File::Find::name} = $id if not($is_certificate); |
247 $cache{$File::Find::name} = $id if not($is_certificate); |
207 return; |
|
208 } |
|
209 } |
248 } |
210 |
249 |
211 open(FILE, $_) or die "can't open $File::Find::name: $!"; |
250 sub print_usage() { print $USAGE } |
212 my $file = join "", <FILE>; |
|
213 close(FILE); |
|
214 |
|
215 while ($file =~ /^(-+BEGIN CERTIFICATE.*?-+END CERTIFICATE-+)$/msg) { |
|
216 |
|
217 # open filehandles (for read and write) |
|
218 local (*READ, *WRITE); |
|
219 my $cid = open2(\*READ, \*WRITE, @cmd_x509) |
|
220 or die "Can' fork: $!\n"; |
|
221 print WRITE $1; |
|
222 close(WRITE); |
|
223 |
|
224 while (<READ>) { |
|
225 /Signature\sAlgorithm:\s(.*)\s+$/ and $signature = $1; |
|
226 /^subject=\s+(.*)$/ and $subject = $1; |
|
227 /^notAfter=(.*)\s+$/ and $enddate = $1; |
|
228 } |
|
229 close(READ); |
|
230 |
|
231 waitpid($cid, 0) < 0 and die "no child with pid $cid\n"; |
|
232 |
|
233 if ($opt_debug) { |
|
234 print "-----\n"; |
|
235 print "$File::Find::name\n"; |
|
236 print "Signature Algorithm: $signature\n" if ($signature); |
|
237 print "subject: $subject\n" if ($subject); |
|
238 print "enddate: $enddate\n" if ($enddate); |
|
239 } |
|
240 |
|
241 push( |
|
242 @{ $certs{$File::Find::name} }, |
|
243 ($subject, $enddate, $signature) |
|
244 ); |
|
245 |
|
246 $is_certificate = 1; |
|
247 } |
|
248 |
|
249 $known{$File::Find::name} = $id if not($is_certificate); |
|
250 } |
|
251 |
|
252 |
|
253 sub print_usage() { print $USAGE }; |
|
254 |
251 |
255 sub print_help() { |
252 sub print_help() { |
256 print_revision($ME, $VERSION); |
253 print_revision($ME, $VERSION); |
257 print <<EOF; |
254 print <<EOF; |
258 Copyright (c) 2009 Christian Arnold |
255 Copyright (c) 2009 Christian Arnold |
259 |
256 |
260 This plugin checks the expire date for openssl certificates. |
257 This plugin checks the expire date for openssl certificates. |
261 |
258 |
262 $USAGE |
259 $USAGE |
263 -b, --binary <binary> |
260 -b, --binary <binary> |
264 Path of openssl binary (default: /usr/bin/openssl) |
261 Path of openssl binary (default: /usr/bin/openssl) |
265 -d, --directory <path> |
262 -d, --directory <path> |
|
263 |
|
264 … --cache=s db file where the known files are stored |
266 EOF |
265 EOF |
267 |
266 |
268 print "Absolute directory path in which will be recursively search for certificate files (default: /etc).\n"; |
267 print |
|
268 "Absolute directory path in which will be recursively search for certificate files (default: /etc).\n"; |
269 print " -w, --warning <time>\n"; |
269 print " -w, --warning <time>\n"; |
270 print |
270 print |
271 " Certificat should not be more than this time older (default: 1month).\n"; |
271 " Certificat should not be more than this time older (default: 1month).\n"; |
272 print |
272 print |
273 " For time can be used year, month, day, hour, minute, second and weeks.\n"; |
273 " For time can be used year, month, day, hour, minute, second and weeks.\n"; |