File Coverage

blib/lib/fwlog.pm
Criterion Covered Total %
statement 3 253 1.1
branch 0 70 0.0
condition 0 33 0.0
subroutine 1 12 8.3
pod 3 11 27.2
total 7 379 1.8


line stmt bran cond sub pod time code
1             package fwlog;
2 1     1   27573 use Exporter;
  1         2  
  1         5144  
3             @ISA = qw(Exporter);
4             @EXPORT = qw(&Auto &Summary &Protocol &Service);
5             @EXPORT_OK = qw(&Auto &Summary &Protocol &Service);
6             1;
7              
8             $VERSION="1.3";
9              
10             # Set some common regex's.
11             my $ipAddress = qr/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/;
12              
13              
14             # Attempt to auto detect the log type
15             #
16             sub Auto {
17              
18 0     0 1   my $logline = shift;
19              
20 0 0         if ($logline) {
21              
22             #################################################################
23             # Netscreen logs - not sure which version
24             #
25 0 0 0       if ($logline =~ /NetScreen device_id/ && $logline =~ /action=/) {
    0          
    0          
    0          
    0          
    0          
    0          
26 0           return &NetScreen($logline);
27              
28              
29             #################################################################
30             # Checkpoint <= R55 fw log -ftn logs
31             #
32             } elsif ($logline =~
33             /
34              
35             # This should also work for syslog formatted CP logs
36             # which is why there's no start of line anchor "^".
37              
38             \d\d?:\d\d?:\d\d?\s # HH:MM:SS
39             (accept|drop|reject|encrypt|decrypt)\s # action
40              
41             /x) {
42              
43 0           return &Checkpoint($logline);
44              
45              
46             #################################################################
47             # Cisco Pix logs v6.x and possibly others
48             #
49             } elsif ($logline =~ /%(PIX|FWSM)-\d+-\d+:/) {
50 0           return &Pix($logline);
51              
52              
53             #################################################################
54             # IPCHAINS
55             } elsif ($logline =~ /Packet log:/) {
56 0           return &Ipchains($logline);
57              
58              
59             #################################################################
60             # IPTABLES with FWBuilder
61             #
62             } elsif ($logline =~ /kernel:\sRULE\s\d+\s--/) {
63 0           return &Fwbuilder($logline);
64              
65              
66             #################################################################
67             # ipf
68             #
69             } elsif ($logline =~ /ipmon\[\d+\]:\s/) {
70 0           return &Ipfilter($logline);
71              
72              
73             #################################################################
74             # pfSense
75             #
76             } elsif ($logline =~ /\spf:\s/) {
77 0           return &pfSense($logline);
78              
79              
80             #################################################################
81             # Unable to auto detect this line
82             #
83             } else {
84 0           return undef;
85             }
86             }
87             }
88              
89              
90             # Allow Summary function for backwards compatibility
91             #
92             sub Summary {
93 0     0 0   &Auto(shift);
94             }
95              
96              
97             # Return protocol from /etc/protocols file
98             #
99             sub Protocol {
100              
101 0     0 1   my @protocolLine = ();
102 0           my %protocol = ();
103              
104 0 0         if (open (PROTOCOLS, "/etc/protocols")) {
105 0           while () {
106 0 0         unless (/^#/) {
107 0           chomp;
108 0           @protocolLine = split;
109 0           $protocol{$protocolLine[1]} = $protocolLine[0];
110             }
111             }
112 0           close PROTOCOLS;
113              
114             } else {
115 0           warn "Error: could not open /etc/protocols\n";
116 0           return undef;
117             }
118              
119 0           return $protocol{$_[0]};
120             }
121              
122              
123             # Return service from /etc/services file
124             #
125             sub Service {
126              
127 0     0 1   my @servicesLine = ();
128 0           my %services = ();
129              
130 0 0         if (open (SERVICES, "
131 0           while () {
132 0 0         unless (/^#/) {
133 0           chomp;
134 0           @servicesLine = split;
135 0           $services{$servicesLine[1]} = $servicesLine[0];
136             }
137             }
138 0           close SERVICES;
139              
140             } else {
141 0           warn "Error: could not open /etc/services\n";
142 0           return undef;
143             }
144            
145 0           return $services{lc($_[0])};
146              
147             }
148              
149              
150             # pfSense log parser
151             #
152             sub pfSense {
153              
154 0     0 0   my $logline = shift;
155 0           my $action = "";
156 0           my $source="";
157 0           my $destination="";
158 0           my $protocol="";
159 0           my $port="";
160              
161 0 0         if ( $logline =~ /tcp|mss|win \d+$/ ) {
162 0           $protocol = "tcp";
163              
164 0           $logline =~ /\s((pass)|(block))\s/;
165 0           $action = $1;
166 0           $action =~ s/pass/accept/;
167 0           $action =~ s/block/drop/;
168              
169 0           $logline =~ /($ipAddress)\.\d+\s\>\s($ipAddress)\.(\d+)/;
170 0           $source = $1;
171 0           $destination = $2;
172 0           $port = $3;
173              
174 0           return "$action|$source|$destination|$protocol|$port";
175             }
176              
177 0 0 0       if ( $logline =~ /\[\|domain\]$/
      0        
      0        
      0        
      0        
      0        
178             || $logline =~ /\[\|isakmp\]$/
179             || $logline =~ / NTPv\d, /
180             || $logline =~ / SYSLOG /
181             || $logline =~ / UDP, /
182             || $logline =~ / SIP, /
183             || $logline =~ / NBT UDP PACKET/ ) {
184 0           $protocol = "udp";
185              
186 0           $logline =~ /\s((pass)|(block))\s/;
187 0           $action = $1;
188 0           $action =~ s/pass/accept/;
189 0           $action =~ s/block/drop/;
190              
191 0           $logline =~ /($ipAddress)\.\d+\s\>\s($ipAddress)\.(\d+)/;
192 0           $source = $1;
193 0           $destination = $2;
194 0           $port = $3;
195              
196 0           return "$action|$source|$destination|$protocol|$port";
197             }
198              
199 0 0         if ( $logline =~ / ICMP (.+), /) {
200 0           $port = $1;
201 0           $protocol = "icmp";
202              
203 0           $logline =~ /\s((pass)|(block))\s/;
204 0           $action = $1;
205 0           $action =~ s/pass/accept/;
206 0           $action =~ s/block/drop/;
207              
208 0           $logline =~ /($ipAddress)\s\>\s($ipAddress)/;
209 0           $source = $1;
210 0           $destination = $2;
211              
212 0           return "$action|$source|$destination|$protocol|$port";
213             }
214             }
215              
216             sub NetScreen {
217              
218 0     0 0   my $logline = $_[0];
219 0           my $action = "";
220 0           my $source="";
221 0           my $destination="";
222 0           my $protocol="";
223 0           my $port="";
224              
225 0           $logline =~ /\sproto=(\d+)\s/;
226 0           $protocol = &Protocol($1);
227              
228 0 0         if ($protocol eq "icmp") {
229 0           $logline =~ /icmp\stype=(\d+)/;
230 0           $port = "type-$1";
231              
232             } else {
233 0           $logline =~ /dst_port=(\d+)/;
234 0           $port = $1;
235             }
236              
237 0           $logline =~ /.*action=(\w+)\s/;
238 0           $action = $1;
239              
240 0           $logline =~ /.*src=($ipAddress)\s/;
241 0           $source = $1;
242              
243 0           $logline =~ /.*dst=($ipAddress)\s/;
244 0           $destination = $1;
245              
246 0           return "$action|$source|$destination|$protocol|$port";
247             }
248              
249              
250             sub Checkpoint {
251              
252 0     0 0   my $logline = $_[0];
253 0           my $action = "";
254 0           my $source="";
255 0           my $destination="";
256 0           my $protocol="";
257 0           my $port="";
258              
259 0           $logline =~ s/src:/src/g;
260 0           $logline =~ s/dst:/dst/g;
261 0           $logline =~ s/proto:/proto/g;
262 0           $logline =~ s/service:/service/g;
263 0           $logline =~ s/icmp-type:/icmp-type/g;
264 0           $logline =~ s/icmp-code:/icmp-code/g;
265 0           $logline =~ s/;//g;
266 0           $logline =~ s/^[\s\t]+//g;
267              
268 0           $logline =~ /\sproto\s(\w+)/;
269 0           $protocol = lc($1);
270              
271 0 0         if ($protocol eq "icmp") {
272 0           my $icmpType = "unknown";
273 0           my $icmpCode = "unknown";
274              
275 0 0         if ($logline =~ / icmp-type /) {
276 0           $logline =~ /icmp-type\s(\d+)/;
277 0           $icmpType = $1;
278             }
279              
280 0 0         if ($logline =~ / icmp-code /) {
281 0           $logline =~ /icmp-code\s(\d+)/;
282 0           $icmpCode = $1;
283             }
284            
285 0           $port = "(type-$icmpType,code-$icmpCode)";
286              
287             } else {
288 0           $logline =~ /service\s([\d\w\-_]+)/;
289 0           $port = $1;
290             }
291              
292 0           $logline =~ /^[0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}\s(\w+)\s/;
293 0           $action = $1;
294              
295 0           $logline =~ /.*src\s($ipAddress)\s/;
296 0           $source = $1;
297              
298 0           $logline =~ /.*dst\s($ipAddress)\s/;
299 0           $destination = $1;
300              
301 0           return "$action|$source|$destination|$protocol|$port";
302             }
303              
304              
305             sub Pix {
306              
307 0     0 0   my $logline = $_[0];
308 0           my $action = "";
309 0           my $source="";
310 0           my $destination="";
311 0           my $protocol="";
312 0           my $port="";
313              
314             # 6-302013 and 6-302015
315 0 0         if ($logline =~ /
316              
317             %PIX-6-30201[35]:\s
318             Built\s(in|out)bound\s([\d\w]+)\sconnection\s\d+\s
319             for\s.+:(.+)\/(\d+)(?:\s\(.+\))?\s
320             to\s.+:(.+)\/(\d+)\s.*
321              
322             /x) {
323              
324 0           $action="accept";
325 0           $source=$3;
326 0           $destination=$5;
327 0           $protocol=lc($2);
328              
329 0 0         if ($1 eq "in") {
330 0           $port=$6;
331             } else {
332 0           $port=$4;
333             }
334             }
335              
336             # 5-304001
337 0 0         if ($logline =~ /
338              
339             %PIX-5-304001:\s
340             (.+)
341             \sAccessed\sURL\s
342             (.+):
343              
344             /x) {
345              
346 0           $action="accept";
347 0           $source=$1;
348 0           $destination=$2;
349 0           $protocol="tcp";
350 0           $port="80"; # assumed
351             }
352              
353             # 6-106015
354 0 0         if ($logline =~ /
355              
356             %PIX-6-106015:\sDeny\s
357             (\w+)\s\(.+\)\s
358             from\s(.+)\/\d+\s
359             to\s(.+)\/(\d+)\s
360              
361             /x) {
362              
363 0           $action="drop";
364 0           $source=$2;
365 0           $destination=$3;
366 0           $protocol=lc($1);
367 0           $port=$4;
368             }
369              
370             # 3-305005
371 0 0         if ($logline =~ /
372              
373             #%PIX-3-305005:\sNo\stranslation\sgroup\sfound\sfor\s
374             #(\w+)\ssrc\s.+:
375             #(.+)\/\d+\sdst\s.+:
376             #(.+)\/(\d+)
377              
378             %PIX-3-305005:\sNo\stranslation\sgroup\sfound\sfor\s
379             (\w+)\ssrc\s.+:
380             ([-._\d\w]+)(?:\/\d+)?\sdst\s.+:
381             ([-._\d\w]+)(?:\/(\d+)|\s\((.+)\))
382              
383             /x) {
384              
385 0           $action="drop";
386 0           $source=$2;
387 0           $destination=$3;
388 0           $protocol=lc($1);
389 0           $port="$4$5";
390              
391             # make ICMP format the fwlog standard
392 0           $port =~ s/,\s/,/g;
393 0           $port =~ s/\s/-/g;
394             }
395              
396             # 3-106011
397 0 0         if ($logline =~ /
398             %PIX-3-106011:\sDeny\sinbound\s\(No\sxlate\)\s
399             (\w+)\ssrc\s.+:
400             ([-._\d\w]+)(?:\/\d+)?\sdst\s.+:
401             ([-._\d\w]+)(?:\/(\d+)|\s\((.+)\))
402              
403             /x) {
404              
405 0           $action="drop";
406 0           $source=$2;
407 0           $destination=$3;
408 0           $protocol=lc($1);
409 0           $port="$4$5";
410              
411             # make ICMP format the fwlog standard
412 0           $port =~ s/,\s/,/g;
413 0           $port =~ s/\s/-/g;
414             }
415              
416 0 0 0       if ($action && $source && $destination && $protocol && $port) {
      0        
      0        
      0        
417 0           return "$action|$source|$destination|$protocol|$port";
418             } else {
419 0           return undef;
420             }
421             }
422              
423              
424             sub Fwbuilder {
425              
426 0     0 0   my $logline = $_[0];
427 0           my $source="";
428 0           my $destination="";
429 0           my $protocol="";
430 0           my $port="";
431              
432 0           $logline =~ /kernel:\sRULE\s\d+\s\-\-\s(\w+)\s/;
433 0           $action = lc($1);
434 0           $action =~ s/deny/drop/;
435              
436 0           $logline =~ /\sSRC=($ipAddress)\s/;
437 0           $source = $1;
438              
439 0           $logline =~ /\sDST=($ipAddress)\s/;
440 0           $destination = $1;
441              
442 0           $logline =~ /\sDPT=(\d+)\s/;
443 0           $port = $1;
444              
445 0           $logline =~ /\sPROTO=([\d\w]+)\s/;
446 0           $protocol = lc($1);
447              
448 0           return "$action|$source|$destination|$protocol|$port";
449             }
450              
451              
452             sub Ipfilter {
453              
454 0     0 0   my $logline = $_[0];
455 0           my $source="";
456 0           my $destination="";
457 0           my $protocol="";
458 0           my $port="";
459              
460 0           $logline =~ /ipmon\[\d+\]:.*@\d+:\d+\s(\w)\s/;
461 0           $action = lc($1);
462 0 0         if ($action eq "p") {
    0          
463 0           $action="accept";
464             } elsif ($action eq "b") {
465 0           $action="drop";
466             }
467              
468 0           $logline =~ /\sPR\s([\d\w]+)\s/;
469 0           $protocol = $1;
470              
471 0 0         if ($protocol eq "icmp") {
472              
473 0           $logline =~ /\s($ipAddress)\s\-\>\s([$ipAddress)\s.*\sicmp\s([\d\w]+\/[\d\w])\s+/;
474 0           $source = $1;
475 0           $destination =$2;
476 0           $port=$3;
477              
478             } else {
479              
480 0           $logline =~ /\s($ipAddress),(\d+)\s\-\>\s($ipAddress),(\d+)\s/;
481 0           $source = $1;
482 0           my $sourcePort=$2;
483 0           $destination =$3;
484 0           $port=$4;
485              
486 0 0         if ($sourcePort < 1024) {
487 0 0         if ($port > 1023) {
488 0           $port=$sourcePort;
489             }
490             }
491              
492             # test
493 0 0         if ($sourcePort < $port) {
494 0           $port=$sourcePort;
495             }
496             }
497              
498 0           return "$action|$source|$destination|$protocol|$port";
499             }
500              
501              
502             sub Ipchains {
503              
504 0     0 0   my $logline = $_[0];
505 0           my $source="";
506 0           my $destination="";
507 0           my $protocol="";
508 0           my $port="";
509              
510 0           $logline =~ s/[ ]+/ /g;
511              
512 0           $logline =~ /Packet\slog:\s\w+\s([\w\d\-_]+)/;
513 0           $protocol = $1;
514              
515 0           $logline =~ /\sPROTO=(\d+)/;
516 0           $protocol = &Protocol($1);
517              
518 0 0         if ($protocol eq "icmp") {
519 0           my $icmpType = "unknown";
520 0           my $icmpCode = "unknown";
521              
522 0           $logline =~ /\s$ipAddress:(\d)+\s$ipAddress:(\d+)\s/;
523 0           $icmpType = $1;
524 0           $icmpCode = $2;
525 0           $port = "(type-$icmpType,code-$icmpCode)";
526             } else {
527 0           $logline =~ /\s$ipAddress:\d+\s$ipAddress:(\d+)\s/;
528 0           $port = $1;
529             }
530              
531 0 0         if ($logline =~ /REDIRECT/) {
532 0           $logline =~ /\sPacket\slog:\s\w+\s([\w+\d+\-_]+\s[\d\w\-_])/;
533 0           $action = $1;
534              
535             } else {
536 0           $logline =~ /\sPacket\slog:\s\w+\s([\w+\d+\-_]+)\s/;
537 0           $action = $1;
538             }
539              
540 0           $logline =~ /\sPROTO\=\d+\s($ipAddress):\d+\s($ipAddress):\d+\s/;
541 0           $source = $1;
542 0           $destination = $2;
543              
544             # Smoothwall logs drops as a hyphen "-"
545 0 0         if ($action == "-") {
546 0           $action="drop";
547             }
548              
549 0           return "$action|$source|$destination|$protocol|$port";
550             }
551              
552              
553              
554             ################ Documentation ################
555              
556             =head1 NAME
557              
558             fwlog - extract connection data from firewall logs
559              
560              
561             =head1 SYNOPSIS
562              
563             use fwlog
564             $result = fwlog::Auto(...one line of firewall logs...);
565             $result = fwlog::Protocol(protocol number);
566             $result = fwlog::Service(port number/protocol number);
567              
568              
569             =head1 DESCRIPTION
570              
571             B extracts the following data from firewall logs.
572              
573             - Action
574             - Source
575             - Destination
576             - Protocol
577             - Port
578              
579             Data is returned seperated by vertical bars "|". For example
580             "drop|10.1.1.1|192.168.1.1|tcp|25".
581              
582             B resolves IP Protocol numbers to names using your /etc/protocols file
583              
584             B resolves service numbers to names using your /etc/services file and IP protocol number
585              
586             Note: to use fwlog::Service for ICMP types and codes as per RFC-792 add the following to your /etc/services
587              
588             # fwlog services
589             ping-request (type-8,code-0)/icmp
590             ping-reply (type-0,code-0)/icmp
591             network-unreachable (type-3,code-0)/icmp
592             host-unreachable (type-3,code-1)/icmp
593             protocol-unreachable (type-3,code-2)/icmp
594             port-unreachable (type-3,code-3)/icmp
595             frag-needed-but-DF-set (type-3,code-4)/icmp
596             src-route-failed (type-3,code-5)/icmp
597             source-quench (type-4,code-0)/icmp
598             parameter-problem (type-12,code-0)/icmp
599             ttl-excd-in-tran (type-11,code-0)/icmp
600             frag-reass-time-excd (type-11,code-1)/icmp
601             redir-net (type-5,code-0)/icmp
602             redir-host (type-5,code-1)/icmp
603             redir-ToS-and-net (type-5,code-2)/icmp
604             redir-ToS-and-host (type-5,code-3)/icmp
605             timestamp-request (type-13,code-0)/icmp
606             timestamp-reply (type-14,code-0)/icmp
607             info-request (type-15,code-0)/icmp
608             info-reply (type-16,code-0)/icmp
609              
610              
611             =head1 CURRENT SUPPORTED LOG TYPES
612              
613             - Checkpoint Firewall-1
614             - accept
615             - drop
616             - reject
617              
618             - NetScreen
619             - Permit
620             - Deny
621              
622             - CISCO Pix (IOS v6.1 and v6.2 and maybe others)
623             - PIX-6-302013
624             - PIX-5-304001
625             - PIX-6-106015
626             - PIX-3-305005
627             - PIX-3-106011
628              
629             - Smoothwall (v0.9)
630             - only chain logged is by Smoothwall is a hyphen "-".
631              
632             - IPCHAINS
633             - drops
634             - rejects
635             - redirects
636             - custom chains
637              
638             - IPTABLES (using fwbuilder)
639             - drops
640             - accepts
641              
642             - ipf
643             - pass
644             - block
645              
646             - pfSense
647             - pass
648             - block
649              
650             =head1 EXAMPLES
651              
652              
653             =item B
654              
655             use fwlog;
656              
657             while (<>) {
658             chomp;
659             my $data = &fwlog::Auto($_);
660             if ($data eq undef) {
661             $unknownLines{$_}++;
662             next;
663             } else {
664             $events{$data}++;
665             }
666             }
667              
668             print "\n\nConnections:\n";
669             foreach my $event (sort {$events{$b} <=> $events{$a}} keys %events) {
670             print "\t$events{$event}: $event\n";
671             }
672              
673             print "\n\nLines not processed as connection data:\n";
674             foreach my $unknown (sort {$unknownLines{$b} <=> $unknownLines{$a}} keys %unknownLines) {
675             print "\t$unknownLines{$unknown}: $unknown\n";
676             }
677              
678             =item B
679              
680             use fwlog;
681             my $protocol = &fwlog::Protocol("6");
682             print "$protocol\n";
683              
684              
685             =item B
686              
687             use fwlog;
688             my $protocol = &fwlog::Protocol("6");
689             my $service = &fwlog::Service("25/$protocol");
690             print "$protocol, $service\n";
691             my $protocol = &fwlog::Protocol("1");
692             my $service = &fwlog::Service("(type-13,code-0)/$protocol");
693             print "$protocol, $service\n";
694              
695              
696              
697             =head1 AUTHOR
698              
699             Ed Blanchfield
700              
701              
702             =head1 COPYRIGHT AND DISCLAIMER
703              
704             This program is Copyright 2000 by Ed Blanchfield.
705              
706             This program is free software; you can redistribute it and/or
707             modify it under the terms of the Perl Artistic License or the
708             GNU General Public License as published by the Free Software
709             Foundation; either version 2 of the License, or (at your option) any
710             later version.
711              
712             This program is distributed in the hope that it will be useful,
713             but WITHOUT ANY WARRANTY; without even the implied warranty of
714             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
715             GNU General Public License for more details.
716              
717             If you do not have a copy of the GNU General Public License write to
718             the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
719             MA 02139, USA.
720              
721             =cut
722              
723             # Local Variables:
724             # eval: (load-file "pod.en")
725             # End:
726