From 8166912cdd6a5510550917bd1351b9636ab3356e Mon Sep 17 00:00:00 2001
From: Jakub Cegan <cegan@ics.muni.cz>
Date: Thu, 24 Jan 2013 09:14:11 +0100
Subject: [PATCH] (task #613) Warden Watchdog - 1. funkcni verze

Prvni funkcni verze kontroly klientu a udalosti v databazi. Staci nastavit v cronu a provadi predchystane kontroly a reportuje na zadane emaily.
---
 src/warden-server/bin/wardenWatchdog.pl | 156 +++++++++++++++++-------
 1 file changed, 110 insertions(+), 46 deletions(-)

diff --git a/src/warden-server/bin/wardenWatchdog.pl b/src/warden-server/bin/wardenWatchdog.pl
index 0c02581..001a169 100755
--- a/src/warden-server/bin/wardenWatchdog.pl
+++ b/src/warden-server/bin/wardenWatchdog.pl
@@ -7,20 +7,18 @@
 # Use of this source is governed by a BSD-style license, see LICENSE file.
 
 
-use WardenConf;
+#use WardenConf;
 use strict;
 use warnings;
 use DBI;
 use DBD::mysql;
 use DateTime;
-#use Email::Simple;
+use Email::Simple;
 use Sys::Hostname;
-use Text::Wrap;
 use Data::Dumper;
 
-sub sendmailWrapper{
+sub sendmail_wrapper{
   my $message = shift;
-
   if(open(my $sendmail, '|/usr/sbin/sendmail -oi -t')){
     print $sendmail $message;
     close $sendmail;
@@ -33,8 +31,7 @@ sub sendmailWrapper{
 # Array of hashes
 #{query => ; text => ; contact => }
 
-# Get clients admins
-sub sendReport{
+sub send_report{
 
   my $input_data  = shift;
   my $contact    = $$input_data{'contact'};
@@ -49,31 +46,28 @@ sub sendReport{
   }
 
   $domain =~ s/\./\./;
-
   eval{
     $from_hostname = hostname();
-    if(!($from_hostname =~ m/$domain/gi)){
-      $from_hostname .= $domain;
+    if(!($from_hostname =~ m/^$domain$/gi)){
+      $from_hostname = $domain;
     }
-  };
-  if($@){
+    1;
+  } or do {
     return (0, "Can't retrive hostname for 'From' header!\n");
-  }
+  };
 
   eval{
-  #$message = Email::Simple->create(
-    #header => [
-      #To                    => $contact,
-      #From                  => 'warden_watchdog@'.$from_hostname,
-      #Subject               => 'Kotrola stavu udalosti na Wardenu'],
-      #body => fill('','',$text));
-  };
-  if($@){
+    $message = Email::Simple->create(
+      header => [
+        To      => $contact,
+        From    => 'warden_watchdog@'.$from_hostname,
+        Subject => "Kotrola stavu udalosti warden serveru na stroji $domain"],
+      body  => $text);
+  } or do {
     return (0, "Can't create email message\n");
-  }
+  };
 
-  print "== $contact ==\n$text\n";
-  my ($rc, $err) = 1;#sendmailWrapper($message->as_string);
+  my ($rc, $err) = sendmail_wrapper($message->as_string);
   if(!$rc){
     return (0, $err);
   }
@@ -100,7 +94,32 @@ sub connect_to_DB {
   }
 }
 
-sub sendQuery{
+
+sub update_procedures{
+
+  my $procRef = shift;
+
+  my @procedures = @{$procRef};
+  my $dbh;
+
+  # connect to DB
+  my ($rc,$err) = connect_to_DB(\$dbh);
+  if (!$rc){
+    return (0,'update_procedures can\'t connect do DB: '.$err);
+  }
+
+  foreach my $proc (@procedures) {
+    $dbh->do($proc);
+  }
+
+  # disconnect to DB
+  $dbh->disconnect;
+
+  return 1;
+}
+
+
+sub send_query{
 
   my $configRef = shift;
   my $eventsRef = shift;
@@ -114,12 +133,10 @@ sub sendQuery{
   # connect to DB
   ($rc,$err) = connect_to_DB(\$dbh);
   if (!$rc){
-    return (0, $err);
+    return (0,'send_query can\'t connect do DB: '.$err);
   }
 
   while ($i < scalar(@config)) {
-    my $contact;
-
     # run DB query -> requestor, client name
     my $sth;
     if (defined($config[$i]{query})){
@@ -134,15 +151,27 @@ sub sendQuery{
     };
 
     my @result;
+    my $contact;
+    my $msg_text = 1;
     while(@result = $sth->fetchrow()){
       if (defined($config[$i]{contact})){
         $contact = $config[$i]{contact};
+        if($msg_text){
+          $bad_events{$contact} .= $config[$i]{text} . "\n\n";
+          $msg_text = 0;
+        }
+        $bad_events{$contact} .= join(", ",@result) . "\n";
       }
       else{
         $contact = "from_db\@$result[0]";
+        $bad_events{$contact} .= $config[$i]{text} . "\n\n";
+        $bad_events{$contact} .= join(", ",@result) . "\n";
       }
-      $bad_events{$contact} .= $config[$i]{text} . "DB INFO: ". join(', ',@result) ."\n";
     }
+    foreach my $key (keys %bad_events){
+      $bad_events{$key} .= "\n\n";
+    }
+
     $sth->finish;
     $i++;
   }
@@ -166,40 +195,75 @@ sub run{
     my $dt = DateTime->now();
     $dt = DateTime->now()->subtract(days => $period);
     $date = $dt->date();
+  } or do {
+    #print "Warden watchdog - can't work with date\n";
+    syslog("err|Warden watchdog - can't work with date\n");
   };
-  if($@){
-    print "Warden watchdog - can't work with date\n";
-    #syslog("err|Warden watchdog - can't work with date\n");
-  }
 
+  # stored procedures
+  # iptest - is ip from private network
+
+  my @procedures = ('DROP FUNCTION IF EXISTS iptest;', 'CREATE FUNCTION iptest(ip VARCHAR(15)) RETURNS TINYINT(1) DETERMINISTIC
+BEGIN
+    SET  @nip = INET_ATON(ip);
+    IF(
+        ISNULL( @nip)                           OR
+         @nip BETWEEN          0 AND   16777216 OR
+         @nip BETWEEN  167772160 AND  171966464 OR
+         @nip BETWEEN 2130706432 AND 2130706433 OR
+         @nip BETWEEN 2851995648 AND 2851995649 OR
+         @nip BETWEEN 2886729728 AND 2886729729 OR
+         @nip BETWEEN 3221225472 AND 3221225473 OR
+         @nip BETWEEN 3221225984 AND 3221225985 OR
+         @nip BETWEEN 3227017984 AND 3227017985 OR
+         @nip BETWEEN 3232235520 AND 3232235521 OR
+         @nip BETWEEN 3323068416 AND 3323068417 OR
+         @nip BETWEEN 3325256704 AND 3325256705 OR
+         @nip BETWEEN 3405803776 AND 3405803777 OR
+         @nip BETWEEN 3758096384 AND 3758096385 OR
+         @nip BETWEEN 4026531840 AND 4026531841 OR
+         @nip    >    4294967295) THEN
+            RETURN TRUE;
+        ELSE
+            RETURN FALSE;
+        END IF;
+END;');
+
+  # {query => ; text => ; contact => }
+  # Time of last event of each client.
+  # Is it unsupported type of event?
+  # Is event from the future?
+  # Martane - Is IP from the private network?
   my @configuration = (
-  {query => "SELECT hostname, service, MAX(received) FROM events WHERE valid = 't' GROUP BY hostname, service ORDER BY MAX(received) ASC;", text => "Hey, this is test of warning for admin!\n"},
-  {query => "SELECT requestor FROM clients WHERE service IN (SELECT service FROM events WHERE detected > '$date' AND type NOT IN ('portscan', 'bruteforce', 'probe', 'spam', 'phishing', 'botnet_c_c', 'dos', 'malware', 'copyright', 'webattack', 'test', 'other') AND valid = 't' GROUP BY service) GROUP BY requestor;", text => "Hey, this is test of warning!\n", contact => 'warden-administrator@cesnet.cz'});
-
-  $Text::Wrap::columns = 80;
+  {query => "SELECT hostname, service, MAX(received) FROM events WHERE valid = 't' GROUP BY hostname, service ORDER BY MAX(received) ASC;", text => "Uvedeny klient, nebo klienti jiz delsi dobu nereportovali zadne udalosti do Wardenu. Je mozne, ze nefunguji spravne.", contact => 'jakubcegan@cesnet.cz, ph@cesnet.cz'},
+  {query => "SELECT requestor FROM clients WHERE service IN (SELECT service FROM events WHERE detected > '$date' AND type NOT IN ('portscan', 'bruteforce', 'probe', 'spam', 'phishing', 'botnet_c_c', 'dos', 'malware', 'copyright', 'webattack', 'test', 'other') AND valid = 't' GROUP BY service) GROUP BY requestor;", text => "Uvedeny klient, nebo klienti zasilaji nepodporovany nebo zastaraly typ udalosti na server Warden", contact => 'jakubcegan@cesnet.cz, ph@cesnet.cz'},
+  {query => "SELECT hostname, service, type, COUNT(*) FROM events WHERE detected - received > 0 AND received > '$date' GROUP BY hostname, service, type;", text => "Uvedeny klient, nebo klienti odesilaji odesilaji udalosti s casem z budoucnosti. Cas prirazeny serverem pri prichodu udalosti (received) musi byt vzdy roven nebo vetsi casu detekce (detected).", contact => 'jakubcegan@cesnet.cz, ph@cesnet.cz'},
+  {query => "SELECT hostname, service, received, source, count(source) AS c, min(received), max(received) FROM events WHERE valid = 't' AND source_type = 'IP' AND iptest(source) GROUP BY hostname, service, source ORDER BY c DESC;", text => "Uvedeni klient, nebo klienti odesilaji udalosti se zdrojovou adresou, ktera by se nemela objevit v internetu (privatni rozsah), nebo je neplatna (prazdny oktet, oktet je vetsi nez 255, apod.). kvuli omezeni verzi MySQL serveru funguje zatim pouze pro IPv6.", contact => 'jakubcegan@cesnet.cz, ph@cesnet.cz'});
 
+  my ($rc,$err) = update_procedures(\@procedures);
+  if (!$rc){
+    #print "Warden watchdog - $err\n";
+    syslog("err|Warden watchdog - $err\n");
+  }
 
   my %bad_events;
-
   my $i = 0;
   while ($i < scalar(@configuration)) {
-    my ($rc,$err) = sendQuery(\@configuration,\%bad_events);
+    my ($rc,$err) = send_query(\@configuration,\%bad_events);
     if (!$rc){
-      print "Warden watchdog - $err\n";
-      #syslog("err|Warden watchdog - $err\n");
+      #print "Warden watchdog - $err\n";
+      syslog("err|Warden watchdog - $err\n");
     }
     $i++;
   }
 
   while (my ($contact, $text) = each(%bad_events)){
     my %input = (contact => $contact, domain => $domain, text => $text);
-    my ($rc,$err) = sendReport(\%input);
+    my ($rc,$err) = send_report(\%input);
     if (!$rc){
-      # TODO syslog
-      print $err;
-      #syslog("err|Warden client - networkReporter $err\n");
+      #print $err;
+      syslog("err|Warden client - networkReporter $err\n");
     }
-    print "\n\n";
   }
 }
 
-- 
GitLab