Added log analysis for spam and abuse in sendmail log entries.
[gnucomo.git] / src / gcm_daemon / classes / gnucomo.process_log.php
1 <?PHP
2
3 /**********************************************************************************
4 **  (c) Copyright 2002, Brenno J.S.A.A.F. de Winter, De Winter Information Soltions
5 ** This is free software; you can redistribute it and/or modify it under the
6 ** terms of the GNU General Public License, see the file COPYING.
7 ***********************************************************************************/
8
9 function linux_log ()
10 {
11   /* The function linux_log will seperate the logline in several elements. This will
12    * ease the work of recognizing the type of logline. Once this has been detected
13    * the correct module will start using the data for a log_adv-table.
14    * INPUT    : NONE
15    * GLOBALS  : $dbms (database class containing the logline)
16    * OUTPUT   : Status of success ('TRUE' for success and 'FALSE' for failure
17    */
18
19    global $dbms;
20    global $developrelease;
21
22    $local_log_string = str_replace("  ", " ", $dbms->db_result_row[6]);
23    $local_logline_array = explode (" ", $local_log_string);
24
25    $service_type = $dbms->db_result_row[3];
26    switch (strtolower($service_type))
27    {
28    case "kernel":
29       //This is a kernel logline now discover which type kernel-record we have
30
31       //Detect if this is a network-line
32       if (strtolower(substr($local_logline_array[5],0,3)) == "in=")
33       {
34          //this is a networkline call the processing the routines
35          $local_result = linux_kernel_network();
36          return $local_result;
37       }
38       else
39       {
40
41          //This line is a kernel line writing about a device.
42          if (strtolower($local_logline_array[4]) == 'device')
43          {
44             echo $local_log_string;
45
46             $local_result = linux_kernel_device();
47             return $local_result;
48          }
49          else
50          {
51             if ($developrelease == 'TRUE')
52             {
53
54                $local_failing_string = "Failing string: ".$dbms->db_result_row[5];
55                syslog (LOG_INFO, "Unrecognized kernelline:".$local_log_string);
56                syslog (LOG_INFO, $local_failing_string);
57             }
58             return "FALSE";
59          }
60       }
61       break;
62
63    case "anacron":
64       $local_result = linux_daemon();
65       break;
66
67    case "apmd":
68       $local_result = linux_daemon();
69       break;
70
71    case "atd":
72       $local_result = linux_daemon();
73       break;
74
75    case "crond":
76       $local_result = linux_daemon();
77       break;
78
79    case "httpd":
80       $local_result = linux_daemon();
81       break;
82
83    case "lpd":
84       $local_result = linux_daemon();
85       break;
86
87    case "mysqld":
88       $local_result = linux_daemon();
89       break;
90
91    case "postfix":
92       $local_result = linux_daemon();
93       break;
94
95    case "random":
96       $local_result = linux_daemon();
97       break;
98
99    case "rhnsd":
100       $local_result = linux_daemon();
101       break;
102
103    case "sendmail":
104       $local_result = linux_daemon_sendmail();
105       break;
106
107    case "syslog":
108       $local_result = linux_daemon();
109       break;
110
111    case "syslogd":
112       $local_result = linux_daemon();
113       break;
114
115    case "xinetd":
116       $local_result = linux_daemon();
117       break;
118
119    default:
120       break;
121    }
122 }
123
124 function linux_daemon_sendmail()
125 {
126
127    /* This function is able to deal with the logs delivered by MTAs
128     * the following are currently supported:
129     * - sendmail
130     * INPUT   : NONE
131     * GLOBALS : $dbms, $dbms_working
132     * OUTPUT  : "TRUE" for success and "FALSE" for failure.
133     */
134
135    global $dbms;
136    global $dbms_working;
137
138     //Basic processing.
139
140     //Determine the type of records
141     //When this is sendmail find the beginning by chopping everything into
142     //little pieces.
143     $local_log_string = str_replace("  ", " ", $dbms->db_result_row[6]);
144     //echo " Processing " . $local_log_string . "\n";
145     $local_logline_array = explode (" ", $local_log_string);
146
147     $local_sql_1 = "INSERT INTO log_adv_daemon_email"; //BASIC STATEMENT
148     $local_sql_2 = "logid, detailed_table, service"; //FIELDS
149     $local_sql_3 = "'".$dbms->db_result_row[0]."', 'log_adv_daemon_email', 'sendmail'"; //VALUES
150
151     $message_id = trim($local_logline_array[5], " \t:");
152
153     if ($message_id == 'NOQUEUE')
154     {
155        //  This is an error rather than a real message id.
156
157        $local_sql_2 .= ", event";
158        $local_sql_3 .= ", '". $message_id ."'";
159
160        //  Try to find the source IP address in the 6th or 7th word.
161
162        $source_ip = strstr($local_logline_array[6], "[");
163        if ($source_ip)
164        {
165           $end =  strpos($source_ip, "]");
166           $source_ip = substr($source_ip, 1, $end - 1);
167        }
168        else
169        {
170           $source_ip = strstr($local_logline_array[7], "[");
171           if ($source_ip)
172           {
173              $end =  strpos($source_ip, "]");
174              $source_ip = substr($source_ip, 1, $end - 1);
175           }
176        }
177
178        if ($source_ip)
179        {
180           // We found a source IP address
181
182           $local_sql_2 .= ", source_ip";
183           $local_sql_3 .= ", '". $source_ip ."'";
184        }
185        else
186        {
187           echo "Sendmail error NOQUEUE but no source IP found. logid = " .$dbms->db_result_row[0] . "\n";
188        }
189     }
190     else
191     {
192        $local_sql_2 .= ", internal_messageid";
193        $local_sql_3 .= ", '". $message_id ."'";
194        $local_len   = 0;
195        $local_id    = 0;
196    
197        for ($i = 6; $i <= ( count($local_logline_array) - 1); $i++)
198        {
199    
200            //Get rid of the nasty comma's at the end
201            if ( substr($local_logline_array[$i], strlen($local_logline_array[$i])-1, 1)  == "," )
202            {
203               $local_dummylength = strlen($local_logline_array[$i]) -1;
204               $local_dummy = substr ($local_logline_array[$i], 0,$local_dummylength );
205               $local_logline_array[$i] = trim($local_dummy);
206            }
207    
208            if (substr($local_logline_array[$i],0,1) == '[' && strstr($local_sql_2, "source_ip") == false)
209            {
210              $local_dummy = trim($local_logline_array[$i], " []:");
211              $local_sql_2 .= ", source_ip";
212              $local_sql_3 .= ", '$local_dummy'";
213            }
214            else if (strstr($local_logline_array[$i], "="))
215            {
216    
217               $local_element = explode("=", $local_logline_array[$i]);
218    
219               switch (strtolower($local_element[0]))
220               {
221               case "from":
222                  $local_sql_2 .= ", from_email";
223                  $local_sql_3 .= ", '". addslashes($local_element[1]) ."'";
224                  break;
225    
226               case "relay":
227                  $local_sql_2 .= ", relay";
228                  $local_sql_3 .= ", '". addslashes(trim($local_element[1], " []")) ."'";
229                  break;
230    
231               case "size":
232                  $local_sql_2 .= ", size";
233                  $local_sql_3 .= ", '".$local_element[1]."'";
234                  break;
235    
236               case "delay":
237                  $local_sql_2 .= ", delay";
238                  $local_sql_3 .= ", '".ereg_replace("\+", " ", $local_element[1])."'";
239                  break;
240    
241               case "xdelay":
242                  $local_sql_2 .= ", xdelay";
243                  $local_sql_3 .= ", '".ereg_replace("\+", " ", $local_element[1])."'";
244                  break;
245    
246               case "mailer":
247                  $local_sql_2 .= ", mailer";
248                  $local_sql_3 .= ", '".$local_element[1]."'";
249                  break;
250    
251               case "dsn":
252                  $local_sql_2 .= ", dsn";
253                  $local_sql_3 .= ", '".$local_element[1]."'";
254                  break;
255    
256               case "msgid":
257                  $local_sql_2 .= ", external_messageid";
258                  if (substr($local_element[1],0,1) == '<')
259                  {
260                     $local_sql_3 .= ", '";
261                     $local_sql_3 .= substr($local_element[1],1,(strlen($local_element[1])-2));
262                     $local_sql_3 .= "'";
263                  }
264                  else
265                  {
266                     $local_sql_3 .= ", '".$local_element[1]."'";
267                  }
268                  break;
269    
270               //As of this point we only deal with Status
271               case "stat":
272                  $local_sql_2 .= ", status";
273                  $local_sql_3 .= ", '".$local_element[1]."'";
274    
275                  $local_pos = strrpos (strtolower($local_logline_array[$i]), "stat=");
276                  $local_len = strlen($local_logline_array[$i]) - $local_pos - 6;
277                  $local_sql_2 .= ", status_details";
278                  $local_sql_3 .= ", '".substr($local_logline_array[$i], $local_pos + 5, $local_len) . "'";
279                  break;
280    
281               case "status":
282                  $local_sql_2 .= ", status";
283                  $local_sql_3 .= ", '".$local_element[1]."'";
284    
285                  $local_pos = strrpos (strtolower($local_logline_array[$i]), "status=");
286                  $local_len = strlen($local_logline_array[$i]) - $local_pos - 8;
287                  $local_sql_2 .= ", status_details";
288                  $local_sql_3 .= ", '".substr($local_logline_array[$i], $local_pos + 7, $local_len) . "'";
289                  break;
290    
291               case "reject":
292                  if ($local_element[1] == "550")
293                  {
294                     $local_sql_2 .= ", event";
295                     $local_sql_3 .= ", 'SPAM'";
296                  }
297                  else if ($local_element[1] == "553")
298                  {
299                     $local_sql_2 .= ", event";
300                     $local_sql_3 .= ", 'Blocked SPAM'";
301                  }
302                  else
303                  {
304                     echo "Unknown reject code in sendmail log: " . $local_element[1] .
305                             ", logid = " .$dbms->db_result_row[0] . "\n";
306                  }
307                  break;
308
309               case "POSSIBLE":
310                  echo "POSSIBLE ATTACK special report: $local_log_string\n";
311                  break;
312
313               default:
314                  if (substr(strtolower($local_element[0]),0,1) == "[")
315                  {
316                    $local_sql_2 .= ", destination_ip";
317                    $local_sql_3 .= ", '". substr($local_element[1], 1, strlen($local_element[1]) - 2)."'";
318                  }
319                  break;
320               }
321            }
322        }
323     }
324
325     //Now that the data is complete create the SQL-statement
326     $local_sql = $local_sql_1." (".$local_sql_2.") VALUES (".$local_sql_3.")";
327     $dbms_working->query($local_sql);
328
329     RETURN "TRUE";
330 }
331
332 function linux_kernel_network()
333 {
334
335    /* This function is able to deal with the output of kernel-network messages
336     * coming from iptables and other similar tools. When elements are found
337     * that cannot be identified a notification will be written to the logbook
338     * for easy expansion of this routine.
339     * INPUT    : NONE
340     * GLOBALS  : $dbms, $dbms_working;
341     * OUTPUT   : "TRUE" for success and "FALSE" for failure.
342     */
343
344     global $dbms;
345     global $dbms_working;
346
347     $local_log_string = str_replace("  ", " ", $dbms->db_result_row[6]);
348     $local_logline_array = explode (" ", $local_log_string);
349     $local_sql_1 = "INSERT INTO log_adv_kernel_network"; //BASIC STATEMENT
350     $local_sql_2 = "logid, detailed_table"; //FIELDS
351     $local_sql_3 = "'".$dbms->db_result_row[0]."', 'log_adv_kernel_network'"; //VALUES
352     $local_len   = 0;
353     $local_id    = 0;
354     $local_tos   = "F";
355
356     for ($i = 4; $i <= ( count($local_logline_array) - 1); $i++)
357     {
358         $local_element = explode("=", $local_logline_array[$i]);
359         switch (strtolower($local_element[0]))
360         {
361           case "in":
362             $local_sql_2 .= ", device_in";
363             $local_sql_3 .= ", '".$local_element[1]."'";
364             break;
365
366           case "out":
367             $local_sql_2 .= ", device_out";
368             $local_sql_3 .= ", '".$local_element[1]."'";
369             break;
370
371           case "mac":
372             $local_sql_2 .= ", hw_address";
373             $local_sql_3 .= ", '".$local_element[1]."'";
374             break;
375
376           case "src":
377             $local_sql_2 .= ", source_ip";
378             $local_sql_3 .= ", '".$local_element[1]."'";
379             break;
380
381          case "dst":
382             $local_sql_2 .= ", destination_ip";
383             $local_sql_3 .= ", '".$local_element[1]."'";
384             break;
385
386          case "len":
387             if ($local_len == 0) {
388                $local_sql_2 .= ", packet_length";
389                $local_len++;
390             } else {
391                $local_sql_2 .= ", body_len";
392             }
393
394             $local_sql_3 .= ", '".$local_element[1]."'";
395             break;
396
397          case "tos":
398             if ($local_tos == "F") {
399                $local_sql_2 .= ", tos_bit";
400                $local_sql_3 .= ", '".$local_element[1]."'";
401             }
402             $local_tos = "T";
403             break;
404
405          case "prec":
406             $local_sql_2 .= ", prec_bit";
407             $local_sql_3 .= ", '".$local_element[1]."'";
408             break;
409
410          case "ttl":
411             $local_sql_2 .= ", ttl";
412             $local_sql_3 .= ", '".$local_element[1]."'";
413             break;
414
415         case "id":
416
417             if ($local_id == 0) {
418                $local_sql_2 .= ", header_id";
419                $local_sql_3 .= ", '".$local_element[1]."'";
420                $local_id = 1;
421             }
422             break;
423
424         case "proto":
425             $local_sql_2 .= ", protocol";
426             $local_sql_3 .= ", '".$local_element[1]."'";
427             if ($local_element[1] == 'ICMP') {
428                $local_icmp = true;
429             }
430             break;
431
432        case "spt":
433             $local_sql_2 .= ", destination_port";
434             $local_sql_3 .= ", '".$local_element[1]."'";
435             break;
436
437        case "dpt":
438             $local_sql_2 .= ", source_port";
439             $local_sql_3 .= ", '".$local_element[1]."'";
440             break;
441
442        case "window":
443             $local_sql_2 .= ", window";
444             $local_sql_3 .= ", '".$local_element[1]."'";
445             break;
446
447        case "urgp":
448            $local_sql_2 .= ", urgp";
449            $local_sql_3 .= ", '".$local_element[1]."'";
450            break;
451
452        case "rst":
453            $local_sql_2 .= ", rst";
454            $local_sql_3 .= ", true";
455            break;
456
457        case "syn":
458            $local_sql_2 .= ", syn";
459            $local_sql_3 .= ", true";
460            break;
461
462        case "df":
463           $local_sql_2 .= ", df";
464           $local_sql_3 .= ", true";
465           break;
466
467        case "type":
468            $local_sql_2 .= ", type";
469            $local_sql_3 .= ", '".$local_element[1]."'";
470            break;
471
472        case "code":
473           $local_sql_2 .= ", code";
474           $local_sql_3 .= ", '".$local_element[1]."'";
475           break;
476
477        case "seq":
478           $local_sql_2 .= ", sequence_number";
479           $local_sql_3 .= ", '".$local_element[1]."'";
480           break;
481
482        case "res":
483           $local_sql_2 .= ", res";
484           $local_sql_3 .= ", '".$local_element[1]."'";
485           break;
486
487        case "[src":
488           /*This record is different. In ICMP information is sometimes returned on an original packet.
489            * When the brackets are used a second line will be added to the
490            * log_adv_kernel_network-table. For that reason the processing into the database will be
491            * done here as well. After that a new insert-string will be created.
492            */
493
494           //Enter the data into the database
495           $local_sql = $local_sql_1." (".$local_sql_2.") VALUES (".$local_sql_3.")";
496           $dbms_working->query($local_sql);
497
498           $local_sql_1 = "INSERT INTO log_adv_kernel_network"; //BASIC STATEMENT
499           $local_sql_2 = "logid, detailed_table"; //FIELDS
500           $local_sql_3 = "'".$dbms->db_result_row[0]."', 'kernel_network'"; //VALUES
501           $local_len   = 0;
502           $local_id    = 0;
503           break;
504       default:
505 /*           $local_element[0];
506            syslog(LOG_INFO, "Unrecognized kernel/network entry: ".$local_element[0]);
507
508 */
509      }
510
511    }
512
513
514    //Now that the data is complete create the SQL-statement
515    $local_sql = $local_sql_1." (".$local_sql_2.") VALUES (".$local_sql_3.")";
516    $dbms_working->query($local_sql);
517    RETURN "TRUE";
518
519 }
520
521 function linux_kernel_device()
522 {
523    /* This function is able to deal with the output of kernel-network messages
524     * coming from device related processes. Typically networkcard and other
525     * hardware-related data will show-up here
526     * INPUT    : NONE
527     * GLOBALS  : $dbms, $dbms_working
528     * OUTPUT   : "TRUE" for success and "FALSE" for failure.
529     */
530
531     global $dbms, $dbms_working;
532
533  }
534
535 function linux_daemon()
536 {
537    /* This function is able to deal with the output of kernel-network messages
538     * coming from device related processes. Typically networkcard and other
539     * hardware-related data will show-up here
540     * INPUT    : NONE
541     * GLOBALS  : $dbms, $dbms_working
542     * OUTPUT   : "TRUE" for success and "FALSE" for failure.
543     */
544
545     global $dbms, $dbms_working;
546
547     $local_log_line = strtolower($dbms->db_result_row[6]);
548
549     //Find a sign of stop
550     //Using the word shutdown
551     $pos = strpos($local_log_line, "shutdown");
552     if ($pos > 0)
553     {
554        $local_sql = "INSERT INTO log_adv_daemon (logid, detailed_table, service, event) VALUES ";
555        $local_sql .= "('".$dbms->db_result_row[0]."', 'log_adv_daemon', '"
556                     .$dbms->db_result_row[3]."', 'stop')";
557
558        $dbms_working->query($local_sql);
559     }
560     else
561     {
562       //Using the word stop
563       $pos = strpos($local_log_line, "stop");
564       if ($pos > 0)
565       {
566          $local_sql = "INSERT INTO log_adv_daemon (logid, detailed_table, service, event) VALUES ";
567          $local_sql .= "('".$dbms->db_result_row[0]."', 'log_adv_daemon', '"
568                       .$dbms->db_result_row[3]."', 'stop')";
569          $dbms_working->query($local_sql);
570       }
571       else
572       {
573         //As the word restart
574         $pos = strpos($local_log_line, "restart");
575         if ($pos > 0)
576         {
577           $local_sql = "INSERT INTO log_adv_daemon (logid, detailed_table, service, event) VALUES ";
578           $local_sql .= "('".$dbms->db_result_row[0]."', 'log_adv_daemon', '"
579                        .$dbms->db_result_row[3]."', 'stop')";
580           $dbms_working->query($local_sql);
581
582           $local_sql = "INSERT INTO log_adv_daemon (logid, detailed_table, service, event) VALUES ";
583           $local_sql .= "('".$dbms->db_result_row[0]."', 'log_adv_daemon', '"
584                        .$dbms->db_result_row[3]."', 'start')";
585           $dbms_working->query($local_sql);
586         }
587         else
588         {
589           //As the word start this is an else for restart.
590           //If we wouldn't do so restart would also give a positive on start
591           $pos = strpos($local_log_line, "start");
592           if ($pos > 0)
593           {
594             $local_sql = "INSERT INTO log_adv_daemon (logid, detailed_table, service, event) VALUES ";
595             $local_sql .= "('".$dbms->db_result_row[0]."', 'log_adv_daemon', '"
596                          .$dbms->db_result_row[3]."', 'start')";
597             $dbms_working->query($local_sql);
598           }
599           else
600           {
601
602             //The word error indicates problems.
603             $pos = strpos($local_log_line, "error");
604             $pos2 = strpos($local_log_line, "crash"); //The word crash is also considered to be an error
605
606             if ($pos > 0 or $pos2 > 0)
607             {
608                $local_sql = "INSERT INTO log_adv_daemon (logid, detailed_table, service, event) VALUES ";
609                $local_sql .= "('".$dbms->db_result_row[0]."', 'log_adv_daemon', '"
610                             .$dbms->db_result_row[3]."', 'error detected')";
611                $dbms_working->query($local_sql);
612
613                //Quite often an error will be followed with information that the daemon or service ended.
614                $pos = strpos($local_log_line, "abort");
615
616                if ($pos > 0)
617                {
618                   $local_sql = "INSERT INTO log_adv_daemon (logid, detailed_table, service, event) VALUES ";
619                   $local_sql .= "('".$dbms->db_result_row[0]."', 'log_adv_daemon', '"
620                                .$dbms->db_result_row[3]."', 'abort')";
621                   $dbms_working->query($local_sql);
622                }
623                else
624                {
625                  $pos = strpos($local_log_line, "ended");
626                  if ($pos > 0)
627                  {
628                     $local_sql = "INSERT INTO log_adv_daemon (logid, detailed_table, service, event) VALUES ";
629                     $local_sql .= "('".$dbms->db_result_row[0]."', 'log_adv_daemon', '"
630                                  .$dbms->db_result_row[3]."', 'abort')";
631                     $dbms_working->query($local_sql);
632                  }
633                  else
634                  {
635                    $pos = strpos($local_log_line, "stop");
636                    if ($pos > 0)
637                    {
638                       $local_sql = "INSERT INTO log_adv_daemon (logid, detailed_table, service, event) VALUES ";
639                       $local_sql .= "('".$dbms->db_result_row[0]."', 'log_adv_daemon', '"
640                                    .$dbms->db_result_row[3]."', 'abort')";
641                       $dbms_working->query($local_sql);
642                    }
643                    else
644                    {
645
646                      //For power management there is a charge warning
647                      $pos = strpos($local_log_line, "charge");
648                      if ($pos > 0)
649                      {
650                         $local_sql = "INSERT INTO log_adv_daemon (logid, detailed_table, service, event) VALUES ";
651                         $local_sql .= "('".$dbms->db_result_row[0]."', 'log_adv_daemon', '"
652                                      .$dbms->db_result_row[3]."', 'Power warning')";
653                         $dbms_working->query($local_sql);
654                      }
655                      else
656                      {
657
658                        //As the word start this is an else for restart.
659                        //If we wouldn't do so restart would also give a positive on start
660                        //This can only be done if we ensured nothing else was the case
661                        //PLEASE USE THIS AS LATE AS POSSIBLE!!!
662                        $pos = strpos($local_log_line, "exiting");
663                        if ($pos > 0)
664                        {
665                           $local_sql = "INSERT INTO log_adv_daemon (logid, detailed_table, service, event) VALUES ";
666                           $local_sql .= "('".$dbms->db_result_row[0]."', 'log_adv_daemon', '"
667                                        .$dbms->db_result_row[3]."', 'start')";
668                           $dbms_working->query($local_sql);
669                        }
670                      }
671                    }
672                  }
673                }
674             }
675           }
676         }
677       }
678     }
679     return "ok";
680
681 }
682
683 ?>