fixed todos
authorpesch
Fri, 24 Jun 2016 11:55:05 +0200
changeset 39 0f95ea2ef883
parent 38 20874d23e7ed
child 40 b0dce1770a15
fixed todos
lib/Nagios/Check/DNS/check_tlsa_record.pm
t/00-basic.t
t/check_tlsa_record.t
--- a/lib/Nagios/Check/DNS/check_tlsa_record.pm	Wed Jun 22 13:49:16 2016 +0200
+++ b/lib/Nagios/Check/DNS/check_tlsa_record.pm	Fri Jun 24 11:55:05 2016 +0200
@@ -2,10 +2,10 @@
 
 use strict;
 use warnings;
-use feature qw(say switch state);
+use feature qw(say switch);
+use if $ENV{DEBUG} => 'Smart::Comments';
 use Carp;
 use Data::Dumper;
-use if $ENV{DEBUG} => 'Smart::Comments';
 
 #use if $^V >= v5.0.20 => (experimental => gw(smartmatch));
 use experimental qw(smartmatch);
@@ -13,7 +13,10 @@
 
 our $VERSION = '0.1';
 
-my $dane_pattern = qr'(?i)^(?<record>(?<tlsa_usage>\d+)\s+(?<tlsa_selector>\d+)\s+(?<tlsa_match_type>\d+)\s+(?<tlsa_hash>[0-9a-f ]+))$';
+my $dane_pattern =
+qr'(?i)^(?<record>(?<tlsa_usage>\d+)\s+(?<tlsa_selector>\d+)
+\s+(?<tlsa_match_type>\d+)\s+(?<tlsa_hash>[0-9a-f ]+))$'xs;
+#qr'(?i)^(?<record>(?<tlsa_usage>\d+)\s+(?<tlsa_selector>\d+)\s+(?<tlsa_match_type>\d+)\s+(?<tlsa_hash>[0-9a-f ]+))$';
 
 # Alternativly my $tmpfile = File::Temp->new();
 #              unlink($tmpfile);
@@ -22,29 +25,19 @@
 # *global* context. This prevents our garbage collector from closing the
 # file!
 open(my $tmpfile, '+>', undef)
-    or die "Can't open anonymous file: $!\n";
-fcntl($tmpfile, F_SETFD, fcntl($tmpfile, F_GETFD, 0) & ~FD_CLOEXEC) or die "clear FD_CLOEXEC on $tmpfile: $!\n";
+  or die "Can't open anonymous file: $!\n";
+fcntl($tmpfile, F_SETFD, fcntl($tmpfile, F_GETFD, 0) & ~FD_CLOEXEC)
+  or die "clear FD_CLOEXEC on $tmpfile: $!\n";
 sub __tmpfile { $tmpfile }
 my $fdname = '/dev/fd/' . fileno $tmpfile;
 
-
 sub main {
     my $domain   = shift;
     my $port     = shift // 443;
     my $protocol = shift // 'tcp';
     my @validate = validate_tlsa($domain, $port, $protocol);
-    my $length = @validate;
-    my $return = '';
 
-    if ( $length > 1 ) {
-      for ( my $i = 0; $i < $length; $i++) {
-        $return .= "$validate[$i]\n";
-      }
-    }
-    else {
-      $return = $validate[0];
-    }
-      return $return;
+    return join("\n", @validate);
 }
 
 sub get_tlsa_from_dns {
@@ -53,21 +46,17 @@
     my $protocol   = shift // 'tcp';
     my $query      = "dig tlsa _$port._$protocol.$domain +short";
     my @dns_return = qx($query);
-    my $return_length = @dns_return;
-    my $cname;
-
-    for ( my $i = 0; $i < $return_length; $i++)
-    {
+    my @tlsa_line;
 
-      if ($dns_return[$i] =~ /^[_a-z]+[a-z0-9]+/i) {
-        #$dns_return[$i] = "CNAME: $dns_return[$i]";
-        #$dns_return[$i-1] = $dns_return[$i];
-        $dns_return[$i] = $dns_return[$i+1];
-      }
+    foreach my $item (@dns_return) {
+        if ($item !~ /^[_a-z]+[a-z0-9]+/i) {
+            chomp $item;
+            push @tlsa_line, $item;
+        }
     }
 
-    # FIXME: what's about the \n? We should cut it!
-    return @dns_return;
+    return @tlsa_line;
+
 }
 
 sub get_cert {
@@ -85,14 +74,13 @@
     my $same = "< /dev/null 2>/dev/null | openssl x509 -out $fdname 2>&1";
     $cmd .= $same;
 
-    die sprintf "[%s] returned exit:%d signal:%d\n",
-        $cmd, $? >> 8, $? & 0xff
-        if $?;
+    die sprintf "[%s] returned exit:%d signal:%d\n", $cmd, $? >> 8, $? & 0xff
+      if $?;
 
     return qx($cmd);
 }
 
-sub get_tlsa_from_cert {
+sub get_hash_from_cert {
     my $cert          = shift;
     my $hashit        = shift // 'sha256';
     my $tlsa_selector = shift // 1;
@@ -104,9 +92,8 @@
 openssl $hashit
 _
 
-
     if ($tlsa_selector == 0) {
-      $gentlsa = "openssl x509 -in $fdname -outform DER | openssl $hashit";
+        $gentlsa = "openssl x509 -in $fdname -outform DER | openssl $hashit";
     }
 
     my $tlsa_record = qx($gentlsa) or die "nothing found!\n";
@@ -131,11 +118,11 @@
     my $tlsa_match_type;
 
     if ($dig_return =~ /$dane_pattern/) {
-      $tlsa_match_type = $+{tlsa_match_type};
+        $tlsa_match_type = $+{tlsa_match_type};
     }
 
     if ($tlsa_match_type >= 3) {
-       return "Not valid: $tlsa_match_type";
+        return "Not valid: $tlsa_match_type";
     }
 
     for ($tlsa_match_type) {
@@ -152,8 +139,8 @@
     my $dns_tlsa;
 
     if ($dns_return =~ /$dane_pattern/i) {
-      $dns_tlsa = $+{tlsa_hash};
-      $dns_tlsa =~ s/(\S*)\s+(\S*)$/$1$2/;
+        $dns_tlsa = $+{tlsa_hash};
+        $dns_tlsa =~ s/(\S*)\s+(\S*)$/$1$2/;
     }
     return $dns_tlsa;
 }
@@ -186,40 +173,37 @@
     my $tlsa_usage;
 
     if ($dns_return =~ /$dane_pattern/i) {
-      $tlsa_usage = $+{tlsa_usage};
+        $tlsa_usage = $+{tlsa_usage};
     }
     return $tlsa_usage;
 }
 
 sub get_tlsa_selector {
-  #
-  #    0: Full certificate: the Certificate binary structure as defined
-  #          in [RFC5280]
-  #    1: SubjectPublicKeyInfo: DER-encoded binary structure as defined
-  #          in [RFC5280]
-  #         Vorteil: Wenn immer derselbe Private Key für die Generierung von
-  #         Zertifikaten genutzt wird, muss der TLSA-Record nicht mit jedem
-  #         Zertifikatswechsel erneuert werden.
-  #
+    #
+    #    0: Full certificate: the Certificate binary structure as defined
+    #          in [RFC5280]
+    #    1: SubjectPublicKeyInfo: DER-encoded binary structure as defined
+    #          in [RFC5280]
+    #         Vorteil: Wenn immer derselbe Private Key für die Generierung von
+    #         Zertifikaten genutzt wird, muss der TLSA-Record nicht mit jedem
+    #         Zertifikatswechsel erneuert werden.
+    #
     my $dns_return = shift;
     my $tlsa_selector;
 
-
     if ($dns_return =~ /$dane_pattern/i) {
-      $tlsa_selector =  $+{tlsa_selector};
+        $tlsa_selector = $+{tlsa_selector};
     }
 
     return $tlsa_selector;
 
 }
 
-
 sub validate_tlsa {
-    my $domain     = shift;
-    my $port       = shift;
-    my $protocol   = shift;
-    my @dns_return = get_tlsa_from_dns($domain, $port, $protocol);
-    my $length     = @dns_return;
+    my $domain          = shift;
+    my $port            = shift;
+    my $protocol        = shift;
+    my @dns_return      = get_tlsa_from_dns($domain, $port, $protocol);
     my $fail_selector   = 0;
     my $fail_usage      = 0;
     my $fail_match_type = 0;
@@ -228,77 +212,71 @@
     my @tlsa_usage;
     my @tlsa_match_type;
     my @return;
-    my @cname;
 
-    if ($length == 0) {
-      return 'WARNING: No TLSA to check';
+    if (!@dns_return) {
+        return "UNKNOWN: No DANE to check for $domain:$port";
+    }
+
+    my $cert = get_cert($domain, $port);
+    if ($cert =~ /.*unable to load certificate.*/) {
+        return "WARNING: No SSL-Certificate available for $domain:$port";
     }
 
-    my $cert       = get_cert($domain, $port);
+    foreach my $item (@dns_return) {
 
-    if ($cert =~ /.*unable to load certificate.*/) {
-        return "WARNING: No SSL-Certificate for $domain:$port";
-    }
-    my $cert_tlsa  = get_tlsa_from_cert($cert);
-    chomp $cert_tlsa;
-
-    for (my $i = 0; $i < $length; $i++) {
+        my %domain = (
+            'tlsa_dns'        => get_tlsa_dns_record($item),
+            'tlsa_selector'   => get_tlsa_selector($item),
+            'tlsa_usage'      => get_tlsa_usage($item),
+            'tlsa_match_type' => get_tlsa_match_type($item),
+        );
 
-      if ($dns_return[$i] =~ /no tlsa.*$/gi) {
-          return "WARNING: $dns_return[$i]";
-      }
-      #if ($dns_return[$i] =~ /CNAME: .*$/gi) {
-      #  #$dns_return[$i] = $dns_retrun[$i+1];
-      #   $i++;
-      #}
-      if ($dns_return[$i] !~ /CNAME: .*$/gi) {
-        $dns_tlsa[$i] = get_tlsa_dns_record($dns_return[$i]);
-        $tlsa_selector[$i] = get_tlsa_selector($dns_return[$i]);
-        $tlsa_usage[$i] = get_tlsa_usage($dns_return[$i]);
-        $tlsa_match_type[$i] = get_tlsa_match_type($dns_return[$i]);
+        my $tlsa_usage      = $domain{'tlsa_usage'};
+        my $tlsa_selector   = $domain{'tlsa_selector'};
+        my $tlsa_match_type = $domain{'tlsa_match_type'};
+        my $dns_tlsa_hash       = $domain{'tlsa_dns'};
+        my $cert_tlsa_hash;
 
-        if ($tlsa_selector[$i] < 0 or $tlsa_selector[$i] > 1) {
-          $return[$i] = "CRITICAL: TLSA Selector \'$tlsa_selector[$i]\' for $domain:$port is not valid";
-          $fail_selector = 1;
+        if ($tlsa_selector < 0 or $tlsa_selector > 1) {
+            push @return, "CRITICAL: TLSA Selector \'$tlsa_selector\' for "
+            . "$domain:$port is not valid";
+            $fail_selector = 1;
         }
 
-        if ($tlsa_usage[$i] < 0 or $tlsa_usage[$i] > 3) {
-          $return[$i] = "CRITICAL: TLSA Usage \'$tlsa_usage[$i]\' for $domain:$port is not valid";
-          $fail_usage = 1;
+        if ($tlsa_usage < 0 or $tlsa_usage > 3) {
+            push @return, "CRITICAL: TLSA Usage \'$tlsa_usage\' for "
+            . "$domain:$port is not valid";
+            $fail_usage = 1;
         }
 
-        #if ($tlsa_match_type[$i] !~ /not.*(?<mt>\d+)/i)
-        if ($tlsa_match_type[$i] =~ /not.*(?<mt>\d+)/i)
-        {
-          $return[$i] = "CRITICAL: TLSA Match Type  \'$+{mt}\' for $domain:$port is not valid";
-          $fail_match_type = 1;
+        if ($tlsa_match_type =~ /not.*(?<mt>\d+)/i) {
+            push @return, "CRITICAL: TLSA Match Type  \'$+{mt}\' for "
+            . "$domain:$port is not valid";
+            $fail_match_type = 1;
         }
 
         if ($fail_match_type != 1) {
-          $cert_tlsa  = get_tlsa_from_cert($cert,$tlsa_match_type[$i]);
+            $cert_tlsa_hash  = get_hash_from_cert($cert, $tlsa_match_type);
 
-          if ($fail_selector != 1) {
-            $cert_tlsa  = get_tlsa_from_cert($cert,$tlsa_match_type[$i],$tlsa_selector[$i]);
-          }
+            if ($fail_selector != 1) {
+                $cert_tlsa_hash =
+                  get_hash_from_cert($cert, $tlsa_match_type, $tlsa_selector);
+            }
 
-          chomp $cert_tlsa;
-
+            #chomp $cert_tlsa_hash;
         }
 
-        if ($fail_usage != 1 and $fail_selector != 1 and $fail_match_type != 1  ) {
-          if ("$dns_tlsa[$i]" ne "$cert_tlsa") {
-              $return[$i] = "CRITICAL: TLSA Record for $domain:$port is not valid";
-          }
-          else {
-            $return[$i] =  "OK: TLSA Record for $domain:$port is valid";
-          }
+        if ($fail_usage != 1 and $fail_selector != 1 and $fail_match_type != 1)
+        {
+            if ($dns_tlsa_hash ne $cert_tlsa_hash) {
+                push @return,
+                  "CRITICAL: DANE for $domain:$port is not valid";
+            }
+            else {
+                push @return, "OK: DANE for $domain:$port is valid";
+            }
         }
-      }
 
-      else {
-          chomp $dns_return[$i];
-          $return[$i] =  "$dns_return[$i] for $domain:$port";
-      }
     }
 
     return @return;
--- a/t/00-basic.t	Wed Jun 22 13:49:16 2016 +0200
+++ b/t/00-basic.t	Fri Jun 24 11:55:05 2016 +0200
@@ -26,7 +26,9 @@
       )
     {
         my ($domain, $port) = @$_;
-        my (@tlsa) = map { /^_$port._tcp.\S+\s+\d+\s+IN\s+TLSA\s+(.*\n)/i }
+
+        #my (@tlsa) = map { /^_$port._tcp.\S+\s+\d+\s+IN\s+TLSA\s+(.*\n)/i }
+        my (@tlsa) = map { /^_$port._tcp.\S+\s+\d+\s+IN\s+TLSA\s+(.*)\n/i }
           `dig tlsa _$port._tcp.$domain`;
 
     #is get_tlsa_from_dns($domain, $port), $tlsa[0] => "TLSA for $domain:$port";
--- a/t/check_tlsa_record.t	Wed Jun 22 13:49:16 2016 +0200
+++ b/t/check_tlsa_record.t	Fri Jun 24 11:55:05 2016 +0200
@@ -53,8 +53,8 @@
       Nagios::Check::DNS::check_tlsa_record::main(('google.com'));
     like(
         $test_main_no_tlsa,
-        qr(WARNING: .*),
-'main() warning when no SSL-Certificate  or no TLSA-Record/DANE is available'
+        qr(UNKNOWN: .*),
+'main() unknown when no DANE is available'
     );
 
     my $test_main_default_port2 =