check-cert.pl
changeset 0 c163c53b5be4
child 1 acac1ec01fd8
equal deleted inserted replaced
-1:000000000000 0:c163c53b5be4
       
     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 }