dodan override za lintian
[ossec-hids.git] / contrib / ossec2snorby / ossec2snorby.pl
1 #!/usr/bin/perl -w\r
2 use Socket;\r
3 use POSIX 'setsid';\r
4 use strict;\r
5 use ossecmysql;\r
6 # ---------------------------------------------------------------------------\r
7 # Author: Meir Michanie (meirm@riunx.com)\r
8 # Co-Author: J.A.Senger (jorge@br10.com.br)\r
9 # $Id$\r
10 # ---------------------------------------------------------------------------\r
11 # http://www.riunx.com/\r
12 # ---------------------------------------------------------------------------\r
13 #\r
14 # ---------------------------------------------------------------------------\r
15 # About this script\r
16 # ---------------------------------------------------------------------------\r
17 #\r
18 # "Ossec to Snorby" records the OSSEC HIDS alert logs in MySQL database.\r
19 # It can run as a daemon (ossec2snorby.pl), recording in real-time the logs in database or\r
20 # as a simple script (ossec2snorby.pl).\r
21 #\r
22 # ---------------------------------------------------------------------------\r
23 # Snorby support by Jean-Pierre Zurbrugg (jp.zurbrugg@live.com)\r
24 # ---------------------------------------------------------------------------\r
25 # The original script by the Author and Co-author was taken as a template and\r
26 # modified to work with Snorby (http://snorby.org) which uses a Snort DB schema.\r
27 # Credit must go to the author\Co-author for the initial template. \r
28 #\r
29 ########################### WARNING ###############################\r
30 # My modifications are by far stable and worthy of a production environment\r
31 # WHITHOUT INICIAL TWEAKING. Please setup your labs and make sure everything\r
32 # works properly before using this script on a production environment.\r
33 ########################### WARNING ###############################\r
34 #\r
35 #\r
36 #  Changelog:\r
37 #     * Extra validations were added to make sure srcip\dstip do not remain with\r
38 #           a default value of "0" which causes GUI parsing errors if left unhandled.\r
39 #     * Validation against '$hostname'; if localhost then srcip="127.0.0.1"\r
40 #     * Snorby expects IP related data to be published on its "iphdr" table.\r
41 #     * Snorby expects OSSEC's logs to be converted to HEX and wordwrapped.\r
42 #     * Modified the scripts "Daemon" mode; it now uses "tail -Fn 0 <file>"\r
43 #\r
44 # ---------------------------------------------------------------------------\r
45 # Prerequisites\r
46 # ---------------------------------------------------------------------------\r
47 #\r
48 # MySQL Server\r
49 # Perl DBD::mysql module\r
50 # Perl DBI module\r
51 #\r
52 # Snorby prerequesites:\r
53 #   import ossec2snorby_category.sql from contrib directory\r
54 #   Populate "domain" in /etc/ossec2snorby.conf\r
55 #\r
56 # ---------------------------------------------------------------------------\r
57 # Installation steps\r
58 # ---------------------------------------------------------------------------\r
59\r
60 # 1) Create a user to access the database initially created by Snorby;\r
61 # 2) Copy ossec2snorby.conf to /etc/ossec2snorby.conf with 0600 permissions\r
62 # 3) Edit /etc/ossec2snorby.conf according to your needs:\r
63 #   dbhost=localhost\r
64 #   database=snorby\r
65 #   debug=5\r
66 #   dbport=3306\r
67 #   dbpasswd=mypassword\r
68 #   dbuser=ossecuser\r
69 #   daemonize=0\r
70 #   resolve=1\r
71 #   domain=mydomain.local\r
72 #\r
73 # NOTE: It is recommended to keep "resolve" as "1" and populate "domain" with\r
74 #       your actual domain. Not doing so will restrict the script and force it\r
75 #       to populate IPs as "0.0.0.1" for events whose SRC or DST IP are unknown.\r
76 #\r
77 # ---------------------------------------------------------------------------\r
78 # License\r
79 # ---------------------------------------------------------------------------\r
80 #\r
81 # This program is free software; you can redistribute it and/or\r
82 # modify it under the terms of the GNU General Public License\r
83 # as published by the Free Software Foundation; either version 2\r
84 # of the License, or (at your option) any later version.\r
85 #\r
86 # This program is distributed in the hope that it will be useful,\r
87 # but WITHOUT ANY WARRANTY; without even the implied warranty of\r
88 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
89 # GNU General Public License for more details.\r
90 #\r
91 # You should have received a copy of the GNU General Public License\r
92 # along with this program; if not, write to the Free Software\r
93 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\r
94 #\r
95 # ---------------------------------------------------------------------------\r
96 # About OSSEC HIDS\r
97 # ---------------------------------------------------------------------------\r
98 #\r
99 # OSSEC HIDS is an Open Source Host-based Intrusion Detection System.\r
100 # It performs log analysis and correlation, integrity checking,\r
101 # rootkit detection, time-based alerting and active response.\r
102 # http://www.ossec.net\r
103 #\r
104 # ---------------------------------------------------------------------------\r
105 \r
106 # ---------------------------------------------------------------------------\r
107 # Parameters\r
108 # ---------------------------------------------------------------------------\r
109 my $VERSION="0.4";\r
110 $SIG{TERM} = sub { &gracefulend('TERM')};\r
111 $SIG{INT} = sub { &gracefulend('INT')};\r
112 \r
113 # If no ARGV are given then present &help().\r
114 &help() unless @ARGV;\r
115 \r
116 my ($RUNASDAEMON)=0;\r
117 my ($DAEMONLOGFILE)='/var/log/ossec2snorby.log';\r
118 my ($DAEMONLOGERRORFILE) = '/var/log/ossec2snorby.err';\r
119 # Declare OSSEC's log path and filename format.\r
120 my $LOG='/var/ossec/logs/alerts/alerts.log';  # we need to tail this file instead of the old.\r
121                                               # With this we can survive file rotations while\r
122                                               # running in daemon mode.\r
123 \r
124 my ($LOGGER)='ossec2snorby';\r
125 my($OCT) = '(?:25[012345]|2[0-4]\d|1?\d\d?)';\r
126 my($IP) = $OCT . '\.' . $OCT . '\.' . $OCT . '\.' . $OCT;\r
127 my $dump=0;\r
128 my ($hids_id,$hids,$hids_interface,$last_cid)=(undef, 'localhost', 'ossec',0);\r
129 my ($tempvar,$VERBOSE)=(0,0); \r
130 \r
131 my %conf;\r
132 $conf{dbhost}='localhost';\r
133 $conf{database}='snort';\r
134 $conf{debug}=5;\r
135 $conf{dbport}='3306';\r
136 $conf{dbpasswd}='password';\r
137 $conf{dbuser}='user';\r
138 $conf{daemonize}=0;\r
139 $conf{sensor}='sensor';\r
140 $conf{hids_interface}='ossec';\r
141 $conf{resolve}=1;\r
142 $conf{domain}='';\r
143 \r
144 # OSSEC's default class_ID\r
145 # We will categorize events as "unknown" if a match could not be found...\r
146 my $sig_class_id=2;  # We will try to fetch the correct class_id later.\r
147 \r
148 my($sig_class_name,$taillog);\r
149 # ---------------------------------------------------------------------------\r
150 # Arguments parsing\r
151 # ---------------------------------------------------------------------------\r
152 while (@ARGV){\r
153         $_= shift @ARGV;\r
154     if (m/^-d$|^--daemon$/){\r
155         $conf{daemonize}=1;\r
156     }elsif ( m/^-h$|^--help$/){\r
157                 &help();\r
158         }elsif ( m/^-n$|^--noname$/){\r
159                 $conf{'resolve'}=0;\r
160         }elsif ( m/^-v$|^--verbose$/){\r
161          $VERBOSE=1;\r
162     }elsif ( m/^--interface$/){\r
163                 $conf{hids_interface}= shift @ARGV if @ARGV; # ossec-rt/ossec-feed\r
164         }elsif ( m/^--sensor$/){\r
165                 $conf{sensor}= shift @ARGV if @ARGV; # monitor\r
166         }elsif ( m/^--conf$/){\r
167                 $conf{conf}= shift @ARGV if @ARGV; # localhost\r
168         &loadconf(\%conf);\r
169         }elsif ( m/^--dbhost$/){\r
170                 $conf{dbhost}= shift @ARGV if @ARGV; # localhost\r
171         }elsif ( m/^--dbport$/){\r
172                 $conf{dbport}= shift @ARGV if @ARGV; # localhost\r
173         }elsif ( m/^--dbname$/){\r
174                 $conf{database}= shift @ARGV if @ARGV; # snort\r
175         }elsif ( m/^--dbuser$/){\r
176                 $conf{dbuser}= shift @ARGV if @ARGV; # root\r
177         }elsif ( m/^--dbpass$/){\r
178                 $conf{dbpasswd}= shift @ARGV if @ARGV; # monitor\r
179         }\r
180 \r
181 }\r
182 if ($conf{dbpasswd}=~ m/^--stdin$/){\r
183     print "dbpassword:";\r
184     $conf{dbpasswd}=<>;\r
185     chomp $conf{dbpasswd};\r
186 }\r
187 $hids=$conf{sensor} if exists($conf{sensor});\r
188 $hids_interface=$conf{hids_interface} if exists($conf{hids_interface});\r
189 \r
190 # START IN DAEMON MODE ?\r
191 &daemonize() if $conf{daemonize};\r
192 \r
193 my $dbi= ossecmysql->new(%conf) || die ("Could not connect to $conf{dbhost}:$conf{dbport}:$conf{database} as $conf{dbpasswd}\n");\r
194 \r
195 ####\r
196 # SQL vars;\r
197 my ($query,$numrows,$row_ref);\r
198 \r
199 ####\r
200 #get sensor id\r
201 $query= 'select sid,last_cid from sensor where hostname=? and interface=?';\r
202 $numrows= $dbi->execute($query,$hids,$hids_interface);\r
203 if (1==$numrows){\r
204     $row_ref=$dbi->{sth}->fetchrow_hashref;\r
205     $hids_id=$row_ref->{sid};\r
206     $last_cid=$row_ref->{last_cid};\r
207 }else{\r
208     $query="INSERT INTO sensor ( sid , hostname , interface , filter , detail , encoding , last_cid )\r
209 VALUES (\r
210 NULL , ?, ? , NULL , ? , ?, ?\r
211 )";\r
212     $numrows= $dbi->execute($query,$hids,$hids_interface,1,2,0);\r
213     $hids_id=$dbi->lastid();\r
214 }\r
215 $dbi->{sth}->finish;\r
216 &forceprintlog ("SENSOR:$hids; feed:$hids_interface; id:$hids_id; last cid:$last_cid");\r
217 \r
218 my $newrecord=0;\r
219 my %stats;\r
220 my %resolv;\r
221 my ($timestamp,$sec,$mail,$date,$alerthost,$alerthostip,$datasource,$rule,$level,$description,\r
222         $srcip,$dstip,$user,$text,$filtered,$osseclevel)=();\r
223 my $lasttimestamp=0;\r
224 my $delta=0;\r
225 \r
226 &taillog($last_cid,$LOG);\r
227 ################################################################\r
228 sub forceprintlog(){\r
229     $tempvar=$VERBOSE;\r
230     $VERBOSE=1;\r
231     &printlog (@_);\r
232     $VERBOSE=$tempvar;\r
233 }\r
234 \r
235 sub taillog {\r
236    my ($last_cid,$LOG)=@_;\r
237    while (<>) {\r
238     if (m/^$/){\r
239         # we reached a newline, finish up with current record.\r
240         $newrecord=1;\r
241         next unless $timestamp;\r
242         $alerthostip=$alerthost if $alerthost=~ m/^$IP$/;\r
243         \r
244         # Populate DST IP\r
245         if ($alerthostip){\r
246             $dstip=$alerthostip;\r
247             $resolv{$alerthost}=$dstip;\r
248 \r
249         }else{\r
250             if (exists $resolv{$alerthost}){\r
251                 $dstip=$resolv{$alerthost};\r
252             }else{\r
253                 if ($conf{'resolve'}){\r
254                     if ($alerthost =~m/(\d+\.\d+\.\d+\.\d+)/ ){\r
255                         $dstip=$1;\r
256                     }else{\r
257                         # the "host" command doesn't work with Flatname\NetBIOS names.\r
258                         # The server's hostname is almost always a flatname and OSSEC\r
259                         # doesn't return an IP for alerts generated from localhost.\r
260                         # Ex. 2013 Jan 24 15:51:36 ubuntu->/var/log/auth.log                    \r
261                         my $x = `cat /etc/hostname`;  # get Host's hostname\r
262                         chomp $x;  # remove extra lines, if any.\r
263                         if ($x eq $alerthost) {  # Validate if $alerthost is us, localhost.\r
264                             $dstip='127.0.0.1';  # Snorby does not allow empty\"0" as IP value.\r
265                         }else{\r
266                             my $fetch=&host2ip($alerthost);\r
267                         \r
268                             if (defined $fetch){\r
269                                 $dstip=$fetch;\r
270                             }else{\r
271                                 $dstip=$srcip;\r
272                             }\r
273                         }\r
274                     }\r
275                 }\r
276                 $resolv{$alerthost}=$dstip;\r
277             }\r
278         }\r
279         \r
280         #Populate SRC IP (requires dstip to be populated)\r
281         if (! defined $srcip) {\r
282             if (defined $dstip) {\r
283     #Feb  6 15:18:21 1.1.1.1 %ASA-3-313001: Denied ICMP type=3, code=3 from 111.111.1.1 on interface OUTSIDE\r
284                 $filtered =~ s/$dstip//;            # filter out known dstip from log output.\r
285                 $srcip=$1 if $filtered=~ m/\s($IP)\s/;  # Search all text for an IP address.\r
286                                                         # This could easily bug out with logs that contains\r
287                                                         # version numbering such as mysql 1.3.4.5.\r
288             }\r
289     # Windows logs include "User Name" in their log output. Some companies name their employees's PCs after its users which is\r
290     # resolvable via DNS.\r
291     # Windows logs may state a computer name as "User Name". Lets strip out the "$" from the computer name...\r
292             if (defined $user and ! defined $srcip) {\r
293                 my $u=$user;\r
294                 $u=~ s/\$//;  # remove "$" from computer names.\r
295 \r
296                 my $fetch=&host2ip($u);\r
297                 $srcip=$fetch if (defined $fetch);\r
298             }\r
299             if (! defined $srcip or $srcip eq '') {\r
300                 # NO recognizable IPs or User Names were found on log output,\r
301                 # this suggests the log was generated by dstip.\r
302                 $srcip=$dstip;                    \r
303             }\r
304         }\r
305         #\r
306         $last_cid= &prepair2basedata(\r
307             $hids_id,\r
308             $last_cid,\r
309             $timestamp,\r
310             $sec,\r
311             $mail,\r
312             $date,\r
313             $alerthost,\r
314             $datasource,\r
315             $rule,\r
316             $level,\r
317             $description,\r
318             $srcip,\r
319             $dstip,\r
320             $user,\r
321             $text\r
322         );\r
323         ($timestamp,$sec,$mail,$date,$alerthost,$alerthostip,$datasource,$rule,$level,$description,\r
324         $srcip,$dstip,$user,$text)=();\r
325         next ;\r
326     }\r
327     if (m/^\*\* Alert ([0-9]+).([0-9]+):(.*)$/){\r
328         $timestamp=$1;\r
329         if ( $timestamp == $lasttimestamp){\r
330             $delta++;\r
331         }else{\r
332             $delta=0;\r
333             $lasttimestamp=$timestamp;\r
334         }\r
335         $sec=$2;\r
336         $mail=$3;\r
337         $mail=$mail ? $mail : 'nomail';\r
338 #2006 Aug 29 17:19:52 firewall -> /var/log/messages\r
339 #2006 Aug 30 11:52:14 192.168.0.45->/var/log/secure\r
340 #2006 Sep 12 11:12:16 92382-Snort1 -> 172.16.176.132\r
341         }elsif ( m/^([0-9]+\s\w+\s[0-9]+\s[0-9]+:[0-9]+:[0-9]+)\s+(\S+)\s*->(.*)$/){\r
342                 $date=$1;\r
343                 $alerthost=$2;\r
344                 $datasource=$3;\r
345                 if ($datasource=~ m/(\d+\.\d+\.\d+\.\d+)/){\r
346                         $alerthost=$1;\r
347                         $datasource="remoted";\r
348                 }\r
349 #2006 Aug 29 17:33:31 (recepcao) 10.0.3.154 -> syscheck\r
350     }elsif ( m/^([0-9]+\s\w+\s[0-9]+\s[0-9]+:[0-9]+:[0-9]+)\s+\((.*?)\)\s+(\S+)\s*->(.*)$/){\r
351         $date=$1;\r
352         $alerthost=$2;\r
353         $alerthostip=$3;\r
354         $datasource=$4;\r
355     }elsif ( m/^([0-9]+\s\w+\s[0-9]+\s[0-9]+:[0-9]+:[0-9]+)\s(.*?)$/){\r
356                 $date=$1;\r
357                 $alerthost='localhost';\r
358                 $datasource=$2;\r
359     }elsif ( m/Rule: ([0-9]+) \(level ([0-9]+)\) -> \'(.*)\'$/ ){\r
360         $rule=$1;\r
361         $level=$2;\r
362         $osseclevel=$level;  # Keep copy of OSSEC level as it will later be converted to snort level.\r
363         $description= $3;\r
364     }elsif ( m/src\s?ip:/i){\r
365         if ( m/($IP)/){\r
366             $srcip=$1;\r
367         }else{\r
368             $srcip='1';  # Snorby doesn't like srcip\dstip = 0\null.\r
369         }\r
370     }elsif ( m/User: (.*)$/){\r
371         $user=$1;\r
372     }elsif ( m/(.*)$/){\r
373         my $x=$1;\r
374  \r
375         # Get IP from User Name + DNS query.\r
376         if (! defined $user){\r
377              if ( m/User\s?Name:\s?(\S+)\s/i){\r
378                 my $u=$1;\r
379                 $user="$u";\r
380             }\r
381         }\r
382         $x =~ s/(.$)/$1\r\n/;  # lets multiline this string for cleaner output once $payload wordwraps it.\r
383         $text .=$x;\r
384         \r
385         # This variable will be used to populate srcip once we reach the end of the current log entry.\r
386         $filtered=$text;\r
387     }\r
388    } # End of while read line\r
389 }\r
390 \r
391 sub ossec_aton(){\r
392         my ($ip)=@_;\r
393         if ($ip=~ m/(\d+)\.(\d+)\.(\d+)\.(\d+)/){\r
394                 my $num= ($1 * 256 ** 3) + ($2 * 256 ** 2)+ ($3 * 256 ** 1)+ ($4);\r
395 \r
396                 return "$num";\r
397         }else{\r
398                 return "1";  # Snorby has a bug where it wont ouput "N\A" on the IP columns if srcip\dstip = 0\null\r
399         }\r
400 \r
401 }\r
402 \r
403 sub prepair2basedata(){\r
404     my (\r
405         $hids_id,\r
406         $last_cid,\r
407         $timestamp,\r
408         $sec,\r
409         $mail,\r
410         $date,\r
411         $alerthost,\r
412         $datasource,\r
413         $rule,\r
414         $level,\r
415         $description,\r
416         $srcip,\r
417         $dstip,\r
418         $user,\r
419         $text\r
420     )=@_;\r
421     my ($payload,$count,$query,$row_ref,$sig_id);\r
422     \r
423    \r
424 ###\r
425 # Get sig_class_id\r
426 #\r
427 # Note: the original script stated sig_class_id as "1" by default which in BASE might be ok but for \r
428 # snorby it maps to a category of "not-suspicious" which is not efficient when stats are ran based on\r
429 # event categories alone...\r
430 #\r
431 # Furthermore, OSSEC allows a rule to have multiple categories assigned to it which snorby was not prepared to handle GUI\r
432 # wize. (The event details contains a small section where the category value can be shown, this section is too small\r
433 # to show OSSEC's sometimes long category results such as :\r
434 #  syslog,sshd,invalid_login,authentication_failed,)\r
435 #\r
436 # We will use the last category shown only...This isn't the correct approach but my programing skills prevent me\r
437 # from providing a better solution.\r
438 \r
439     # ex: - syslog,sshd,invalid_login,authentication_failed,\r
440     if ($mail=~ m/\.*\,?(\w+\_?\w+)\,?$/){  # $mail contains the rule's categories. No clue why its named $mail...\r
441         $sig_class_name=$1;\r
442         &printlog ("SIG_CLASS_NAME: $sig_class_name \n");\r
443         $query = "SELECT sig_class_id FROM category WHERE cat_name=?";\r
444         $dbi->execute($query,$sig_class_name);\r
445         $count=$dbi->{sth}->rows;\r
446         if ($count){\r
447             $row_ref=$dbi->{sth}->fetchrow_hashref;\r
448             $sig_class_id=$row_ref->{sig_class_id};\r
449             &printlog ("SIG_CLASS_ID: $sig_class_id");\r
450         }else{\r
451             &printlog ("SIG_CLASS_ID NOT FOUND. USING DEFAULT SIG.");\r
452             $sig_class_id=2;\r
453         }\r
454     }else{\r
455         &printlog ("COULD NOT GET A RULE CATEGORY. USING DEFAULT SIG.");\r
456         $sig_class_id=2;\r
457     }\r
458 \r
459 ###\r
460 #\r
461 # Get/Set signature id\r
462 \r
463     # Convert OSSEC Severity to Snort Severity.\r
464     #   Dont modify this after having live data on Snorby as it will create duplicated\r
465     #   sig_names.\r
466     $level=4 if ($level <= 4);  # Informational only.\r
467     $level=3 if ($level == 5);  # User generated errors \ low severity.\r
468     $level=2 if (($level >= 6) && ($level <=11));  # Low to mid severity attacks.\r
469     $level=1 if ($level >= 12);  # High importance events \ Successfull attacks \ all our bases belong to them!\r
470 \r
471     $query = "SELECT sig_id FROM signature where sig_name=? and sig_class_id=? and sig_priority=? and sig_rev=? and sig_sid=? and sig_gid is NULL";\r
472     $dbi->execute($query,$description,$sig_class_id,$level,0,$rule);\r
473     $count=$dbi->{sth}->rows;\r
474     if ($count){\r
475         $row_ref=$dbi->{sth}->fetchrow_hashref;\r
476         $sig_id=$row_ref->{sig_id};\r
477         &printlog ("REUSING SIGNATURE\n");\r
478     }else{\r
479         $query="INSERT INTO signature ( sig_id , sig_name , sig_class_id , sig_priority , sig_rev , sig_sid , sig_gid )\r
480 VALUES (\r
481 NULL ,?, ? , ? , ? , ?, NULL\r
482 )";\r
483         $dbi->execute($query,$description,$sig_class_id,$level,0,$rule);\r
484         $sig_id = $dbi->lastid();\r
485     }\r
486     $dbi->{sth}->finish;\r
487     &printlog ("SIGNATURE: $sig_id\n");\r
488     \r
489     \r
490     ############################\r
491     ############################\r
492         #DEBUG\r
493     # &printlog ("filtered: $filtered \n");\r
494     # &printlog ("SRC IP: $srcip \n");\r
495     # &printlog ("DST IP: $dstip \n");\r
496     # &printlog ("mail: $mail \n");\r
497     # &printlog ("sig_class_name: $sig_class_name");\r
498     # &printlog ("date: $date \n");\r
499     # &printlog ("alerthost: $alerthost \n");\r
500     # &printlog ("rule: $rule \n");\r
501     # &printlog ("level: $level \n");\r
502     # &printlog ("OSSEC level: $osseclevel \n");\r
503     # &printlog ("USER: $user \n");\r
504     # &printlog ("text: $text \n");\r
505     # &printlog ("sec?: $sec \n");\r
506     # &printlog ("datasource: $datasource \n");\r
507     # &printlog ("description: $description \n");\r
508     # exit 1;\r
509     ############################\r
510     \r
511     \r
512 #######\r
513 #\r
514 # Set event\r
515     $query="INSERT INTO event ( sid , cid , signature , timestamp )\r
516 VALUES (\r
517 ? , ? , ? ,? \r
518 )";\r
519     $last_cid++;\r
520     $dbi->execute($query,$hids_id,$last_cid,$sig_id,&fixdate2base($date));\r
521 \r
522     &printlog ("EVENT: ($query,$hids_id,$last_cid,$sig_id,&fixdate2base($date)\n");\r
523     $dbi->{sth}->finish;\r
524 #########\r
525 #\r
526 # Set iphdr\r
527 \r
528     # And yet again,Snorby doesn't like empty IP fields; Validate if srcip or dstip are empty.\r
529     $srcip = "1" if !defined $srcip;  # leaving the fields srcip / dstip with a value = 0\r
530     $dstip = "1" if !defined $dstip;  # causes output to get messed up on the GUI.  \r
531 \r
532     $query=" INSERT INTO iphdr ( sid , cid , ip_src , ip_dst , ip_ver , ip_hlen , ip_tos , ip_len , ip_id , ip_flags , ip_off , ip_ttl , ip_proto , ip_csum )\r
533 VALUES (\r
534 ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ?\r
535 ) ";\r
536     $dbi->execute($query,$hids_id,$last_cid,&ossec_aton($srcip),&ossec_aton($dstip),4,5,0,20,0,0,0,0,0,0);\r
537     &printlog ("iphdr: ($query,$hids_id,$last_cid,&ossec_aton($srcip),&ossec_aton($dstip),4,5,0,undef,undef,undef,undef,undef,undef,undef)\n");\r
538     $dbi->{sth}->finish;\r
539 \r
540 #########\r
541 #\r
542 #\r
543 # Set data\r
544     $payload = "$date ($alerthost) $dstip->$datasource\r\nRule: $rule (OSSEC level $osseclevel) -> $description\r\n$text";\r
545     $payload =~ s/(.{1,109}\S|\S+)\s+/$1\r\n/mg;  # Snorby does not wordwrap the payload, lets wrap the first 109 non-whitespace chars. \r
546     $payload = unpack("H*",$payload);             # Convert to HEX\r
547 \r
548     $query=" INSERT INTO data ( sid , cid , data_payload ) \r
549 VALUES (\r
550 ?,?,?)";\r
551     $dbi->execute($query,$hids_id,$last_cid,$payload);\r
552     &printlog ("DATA: ($query,$hids_id,$last_cid,$payload)\n");\r
553     $dbi->{sth}->finish;\r
554 ##########\r
555 #\r
556     $query="UPDATE sensor SET last_cid=? where sid=? limit 1";\r
557     $numrows= $dbi->execute($query,$last_cid,$hids_id);\r
558 \r
559     $dbi->{sth}->finish;\r
560     return $last_cid;\r
561 } # end sub\r
562 \r
563 sub host2ip {\r
564     # This sub requires argument 0 to be a named host. We also need to know\r
565     # the domain to which we belong to in order to append it to the host if\r
566     # its a flatname host.\r
567     my $host=$_[0];\r
568     my $domain=$conf{domain} if exists($conf{domain});\r
569     my $CMD;\r
570 \r
571     # Validate if we were fed a flatnamed host or a FQDN.\r
572     if ($host =~ m/.*\..+/){\r
573         # FQDN\r
574         $CMD=`host $host 2>/dev/null | grep 'has address' `;\r
575         if ($CMD =~m/(\d+\.\d+\.\d+\.\d+)/ ){\r
576             return($1);\r
577         }else{\r
578             return undef; # return False.\r
579         }\r
580         \r
581     }else{\r
582         # FLATNAME\r
583         if (! defined $domain or $domain eq ''){\r
584             &printlog ('[WARNING]: domain value was not populated on ossec2snorby.conf." . \r
585             " DNS resolutions cannot be completed for NetBIOS\Flatname hosts.');\r
586             return undef;\r
587         }\r
588 \r
589         # There is an extra "." after $domain, this is to ensure linux\r
590         # does not append "localdomain" at the end of the host.\r
591         $CMD=`host $host.$domain. 2>/dev/null | grep 'has address' `;\r
592         if ($CMD =~m/(\d+\.\d+\.\d+\.\d+)/ ){\r
593             return($1);\r
594         }else{\r
595             return undef; # return False.\r
596         }\r
597     }\r
598 }\r
599 \r
600 sub fixdate2base(){\r
601     my ($date)=@_;\r
602     $date=~ s/ Jan /-01-/;\r
603     $date=~ s/ Feb /-02-/;\r
604     $date=~ s/ Mar /-03-/;\r
605     $date=~ s/ Apr /-04-/;\r
606     $date=~ s/ May /-05-/;\r
607     $date=~ s/ Jun /-06-/;\r
608     $date=~ s/ Jul /-07-/;\r
609     $date=~ s/ Aug /-08-/;\r
610     $date=~ s/ Sep /-09-/;\r
611     $date=~ s/ Oct /-10-/;\r
612     $date=~ s/ Nov /-11-/;\r
613     $date=~ s/ Dec /-12-/;\r
614     $date=~ s/\s$//g;\r
615     return $date;\r
616 }\r
617 sub version(){\r
618     print "OSSEC report tool $VERSION\n";\r
619     print "Licensed under GPL\n";\r
620     print "Contributor Meir Michanie\n";\r
621 }\r
622 \r
623 sub help(){\r
624     &version();\r
625     print "This tool helps you import into base the alerts generated by ossec."\r
626         . " More info in the doc directory .\n";\r
627         print "Usage:\n";\r
628         print "$0 [-h|--help] # This text you read now\n";\r
629     print "Options:\n";\r
630     print "\t--dbhost <hostname>\n";\r
631     print "\t--dbname <database>\n";\r
632     print "\t--dbport <[0-9]+>\n";\r
633     print "\t--dbpass <dbpasswd>\n";\r
634     print "\t--dbuser <dbuser>\n";\r
635     print "\t-d|--daemonize\n";\r
636     print "\t-n|--noname\n";\r
637     print "\t-v|--verbose\n";\r
638     print "\t--conf <ossec2based-config>\n";\r
639     print "\t--sensor <sensor-name>\n";\r
640     print "\t--interface <ifname>\n";\r
641     \r
642     exit 0;\r
643 }\r
644 \r
645 \r
646 sub daemonize {\r
647     my $running = kill 0, `cat /var/run/ossec2base2.pid`;\r
648     if ($running){\r
649         print "OSSEC2SNORBY is already running...\n";\r
650         exit 1;\r
651     }\r
652 \r
653     chdir '/'               or die "Can't chdir to /: $!";\r
654     \r
655     open STDOUT, ">>$DAEMONLOGFILE"\r
656                            or die "Can't write to $DAEMONLOGFILE: $!";\r
657 \r
658     # I may be mistaken but the original script didn't seem to actually\r
659     # tail the logs. It would run until it hit EOF and then exit script.\r
660     $taillog= open STDIN,"-|", "/usr/bin/tail", "-Fn 0", "$LOG";\r
661     if ($taillog){\r
662         &forceprintlog ("Daemon started TAIL PID: $taillog");\r
663         &forceprintlog ("NOW MONITORING: $LOG");\r
664     }else{\r
665         &forceprintlog ("Could not start daemon on $LOG: $!");\r
666         exit 1;\r
667     }\r
668 \r
669     defined(my $pid = fork) or die "Can't fork: $!";\r
670     if ($pid){\r
671             open (PIDFILE , ">/var/run/ossec2snorby.pid") ;\r
672             print PIDFILE "$pid\n";\r
673             close (PIDFILE);\r
674             exit 0;\r
675     }\r
676     setsid                  or die "Can't start a new session: $!";\r
677     open STDERR, ">>$DAEMONLOGERRORFILE" or die "Can't write to $DAEMONLOGERRORFILE: $!";\r
678 }\r
679 \r
680 sub gracefulend(){\r
681     my ($signal)=@_;\r
682     &forceprintlog ("Terminating upon signal $signal");\r
683     &forceprintlog ("Daemon halted");\r
684     # This might be paranoid or simply useless but better safe than sorry.\r
685     close STDOUT or die "WARNING: Closing STDOUT failed.";\r
686     close STDERR or die "WARNING: Closing STDERR failed.";\r
687     kill 3, $taillog or die "WARNING: Closing $taillog failed.";\r
688     close STDIN or die "WARNING: Closing STDIN failed.";\r
689     exit 0;\r
690 }\r
691 \r
692 sub printlog(){\r
693     return unless $VERBOSE;\r
694         my (@lines)=@_;\r
695         foreach my $line(@lines){\r
696                 chomp $line;\r
697                 my ($date)=scalar localtime;\r
698                 $date=~ s/^\S+\s+(\S+.*\s[0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}).*$/$1/;\r
699                 print "$date $LOGGER: $line\n";\r
700         }\r
701     }\r
702 \r
703 \r
704 sub loadconf(){\r
705     my ($hash_ref)=@_;\r
706     my $conf=$hash_ref->{conf};\r
707     unless (-f $conf) { &printlog ("ERROR: I can't find config file $conf"); exit 1;}\r
708     unless (open ( CONF , "$conf")){ &printlog ("ERROR: I can't open file $conf");exit 1;}\r
709     while (<CONF>){\r
710         next if m/^$|^#/;\r
711         if ( m/^(\S+)\s?=\s?(.*?)$/) {\r
712                         $hash_ref->{$1} = $2;\r
713                 }\r
714     }\r
715     close CONF;\r
716 }\r