#! /usr/bin/perl -w

# vim:syntax=perl

use strict;
use lib '/usr/local/share/perl5';
use Lire::Syslog;
use Lire::DlfSchema;
use Lire::Program qw( :msg :dlf );
use Lire::Firewall qw/ firewall_number2names /;
use Date::Manip qw(ParseDate UnixDate);

#
# Mapping of FW1-Actions to LIRE-Actions
#
my %action2firewall =
  (
   drop     => "denied",
   reject   => "denied",
   accept   => "permitted",
  );

init_dlf_converter( "firewall" );
my $schema      = Lire::DlfSchema::load_schema( "firewall" );
my $dlf_maker   =
  $schema->make_hashref2asciidlf_func( qw/time action protocol
                                          from_ip from_host from_port rcv_intf
                                          to_ip to_host to_port length rule count msg/);

my $lines      = 0;
my $dlflines   = 0;
my $errorlines = 0;
my %DLF;
my $beginning = 1;

#
# process every line of stdin
#
while (my $line = <>) {
    my %dlf;
    my @fields;
    my %columns;

    $lines++;

    #
    # remove trailing whitespaces and leading pipe-symbols
    #
    $line =~ s/\s*$//g;
    $line =~ s/^\|//g;

    #
    # split log entry into fields seperated by "|"
    #
    @fields = split(/\|/, $line);

    # 
    # process each field and store values in hash
    #
    foreach my $item (@fields) {
      (my $type, my $value) = split(/=/, $item);
      if ($type eq "time") {
        (my $date, my $time) = ($value =~ /^\s*(.+)\s+(.+)$/);
        $columns{"date"} = $date;
        $columns{"time"} = $time;
      } else {
        $columns{$type} = $value;
      }
    }
    
    #
    # skip invalid entries
    #
    next if ((exists($columns{loc})) && (! exists($columns{src})));

    #
    # if the current logfile-entry is a new log entry, send the previous
    # logentry to stdout and process the current entry
    #
    if (exists($columns{loc})) {
      if ($beginning == 0) {
        eval {
          # send previous logentry to stdout
          if ($DLF{length} == 0) {
            $DLF{length} = 1;
          }
          if ($DLF{count} == 0) {
            $DLF{count} = 1;
          }
          my $dlf = $dlf_maker->( \%DLF );
          print join ( " ", @$dlf), "\n";
          $dlflines++;
        };

        if ($@) {
          lr_warn ($@);
          lr_notice( qq{cannot convert line $. "$_" to firewall dlf, skipping} );
          $errorlines++;
        }
        foreach my $DLFentry (keys %DLF) {
          delete($DLF{$DLFentry});
        }
      }

      #
      # process current logentry
      # 

      #
      # ignore ctl-entries
      #
      next if ($columns{action} eq "ctl");
 
      # 
      # convert log-timestamp to unixdate 
      #
      my $dlfdate = ParseDate($columns{date} . " " . $columns{time});
      if (!$dlfdate) {
        exit;
      } else {
        $dlf{time} = UnixDate($dlfdate, "%o");
      }

      #
      # assign log fields to dlf-fields
      #
      $dlf{action} = $action2firewall{$columns{action}} || $columns{action};
      $dlf{protocol} = $columns{proto};
      $dlf{from_ip} = $columns{src};
      $dlf{from_host} = $columns{src};
      $dlf{from_port} = $columns{s_port};
      $dlf{rcv_intf} = $columns{"i/f_name"};
      $dlf{to_ip} = $columns{dst};
      $dlf{to_host} = $columns{dst};
      $dlf{to_port} = $columns{service};
      $dlf{rule} = $columns{rule};
      $dlf{length} = exists($columns{bytes}) ? $columns{bytes} : 0; 
      $dlf{count} = exists($columns{packets}) ? $columns{packets} : 0;
 
      if (exists($columns{"Attack Info"})) {
        $dlf{rule} = "SmartDefense";
        $dlf{msg} = $columns{"Attack Info"};
      }
 
      foreach my $dlfentry (keys %dlf) {
        $DLF{$dlfentry} = $dlf{$dlfentry};
      }
      $beginning = 0;
    } 
    #
    # if the current logfile-entry contains additional infos of the 
    # previous logentry, modify the previous entry
    #
    else {
      if (exists($columns{bytes})) {
        $DLF{length} += $columns{bytes};
      }
      if (exists($columns{packets})) {
        $DLF{count} += $columns{packets};
      }
    } 
}
      
eval {
  #
  # send previous logentry to stdout
  #
  my $dlf = $dlf_maker->( \%DLF );
  print join ( " ", @$dlf), "\n";
  $dlflines++;
};

if ($@) {
  lr_warn ($@);
  lr_notice( qq{cannot convert line $. "$_" to firewall dlf, skipping} );
  $errorlines++;
}

end_dlf_converter( $lines, $dlflines, $errorlines );

__END__

=pod

=head1 NAME

fw1_lea2dlf - convert Check Point FireWall-1 NG logs to firewall DLF

=head1 SYNOPSIS

B<fw1_lea2dlf> I<file>

=head1 DESCRIPTION

B<fw1_lea2dlf> converts Check Point FireWall-1 NG logs into firewall DLF format.
Input for this converter is the output of fw1-loggrabber, a simple 
LEA (Log Export Api) client.

=head1 ACTIVATION INSTRUCTIONS

This DLF converter isn't activated by default in Lire. It requires the
Date::Manip perl module and the fw1-loggrabber tool. Date::Manip can
be installed from CPAN. fw1-loggrabber is available from
http://sourceforge.net/projects/fw1-loggrabber .

If both of these components are installed, you can activate the fw1_lea
DLF conveter by uncommenting its line in the file
${prefix}/etc/lire/address.cf

=head1 OTHER NOTES

Beware!  In case you're getting a lot of warnings complaining about UTF-8
characters, looking like:

 all all UNSET fw1_lea2dlf warning Malformed UTF-8 character
  (unexpected non-continuation byte 0x7a, immediately after
  start byte 0xe3) at /usr/lib/perl5/vendor_perl/5.8.0/Date/Manip.pm
  line 5931.

, and your locale is UTF-8 enabled, there is a workaround: Set the LANG and
LC_CTYPE environment variables to en_US instead of e.g. en_US.UTF-8. You can do
this temporarily within the call of the converter:

 LANG=en_US LC_CTYPE=en_US ./fw12dlf < /your/log/file

=head1 fw1-loggrabber

fw1-loggrabber is build using OPSEC's Software Development Kit.  OPSEC (Open
Platform for Security, http://www.opsec.com/) is an open, multi-vendor security
framework.

Some notes about fw1-loggrabber quoted here.

 ********************************************************
 FW1-LOGGRABBER

 Author:           Torsten Fellhauer
 current Version:  1.0
 ********************************************************

 1) Prerequisites
 2) How to Build
 3) How to Use
 4) Change History

 ********************************************************

 1) Prerequisites

 a) for running FW1-LOGGRABBER

 FW1-LOGGRABBER is statically linked and can therefore be
 run on the following systems:
 * Linux (Tested distributions are Red Hat, SuSE and De-
   bian with Kernel Versions 2.2.x and 2.4.x)
 * Solaris SPARC (Tested versions are Solaris 8 and 9)
  * Windows NT/2000/XP (currently no W32 of FW1-LOGGRABBER
   is available yet and therefore not yet tested)

 b) for building FW1-LOGGRABBER

 FW1-LOGGRABBER uses API-functions from Checkpoints'
 OPSEC SDK. In order to be able to build applications 
 which are using this SDK, a very special build environ-
 ment has to be used. Currently building FW1-LOGGRABBER is
 supported only for Solaris SPARC platform and the Linux
 platform.
 * Linux
  - Red Hat 6.2
  - gcc 2.95.1
  - Checkpoint OPSEC SDK NG-FP3 for Linux 2.2
 * Solaris SPARC
  - Solaris 8
  - gcc 2.95.2
  - Checkpoint OPSEC SDK NG-FP3 for Solaris SPARC
 * Windows
  - EDITME

 3) How to Use

 a) Configure FW1 to enable LEA-Protocol

 In order to be able to use this tool with a Checkpoint
 FW-1 installation, the following tasks have to be done:

 * modify $FWDIR/conf/fwopsec.conf and define the port 
   for unauthenticated lea connections.
     #  lea_server  auth_port   18184
     lea_server       port       50001
 * bounce FW1 (cpstop / cpstart) to activate changes
 * add rule to policy to enable connections on port 
   50001 to the FW-1 Management-Server


 b) Usage of FW1-LOGGRABBER

 FW1-LOGGRABBER is statically linked and therefore not
 dependent of OPSEC libraries. The binary can be run on
 any Linux or Solaris SPARC system.

 Command-Line Options:
  -s             IP-Address of FW1-Management-Server
  -p             unauthenticated LEA-Port of FW1-Server
  -f             exact name of FW1-Logfile or pattern to
                 be matched on FW1-Logfiles.
  --resolve      Resolve IP-Addresses to Names
  --noresolve    Do not resolve IP-Addresses to Names
  --showfiles    Only show available FW1-Logfiles and exit.
  --debug        Enable debug-mode of FW1-LOGGRABBER

 Examples:
 o fw1-loggrabber -s 192.168.2.254 -p 50001 --showfiles
  
  Show all logfiles that are available on the FW1-Manage-
  ment-Server with the IP-Address 192.168.2.254. The LEA-
  Port the Management-Server is listening for unauthenti-
  cated connections is 50001.

 o fw1-loggrabber -s 192.168.2.254 -p 50001

  Show all logentries of the default FW1-Logfile (fw.log)
  on the FW1-Management-Server with the IP-Address
  192.168.2.254 and the LEA-Port 50001.

 o fw1-loggrabber -s 192.168.2.254 -p 50001 -f 2003-03-27_213652.log

  Show all logentries of the specified logfile. If the Log-
  file doesn't exist on the specified FW1-Management-Server,
  no entries are returned.

 o fw1-loggrabber -s 192.168.2.254 -p 50001 -f 2003-03

  Show all logentries of all logfiles on the FW1-Management-
  Server, that contain the pattern "2003-03", i.e. all Log-
  Files from March 2003

 o fw1-loggrabber -s 192.168.2.254 -p 50001 -f fw.adtlog

  Show all logentries of audit logfile on FW1-Management-Server

 4) Change History

 * 1.0 - Initial Version (2003/03/30)
  o get all available FW1-Logfiles
  o get data of one or more FW1-Logfiles

 5) Features to be implemented

 * Implementation of authenticated connections
 * Win32 build




=head1 EXAMPLES

To process a log as produced by FW-1:

 $ fw1-loggrabber -s 192.168.2.254 -p 50001 -f fw.log | fw12dlf 

fw1_lea2dlf will be rarely used on its own, but is more likely
called by lr_log2report:

 $ fw1-loggrabber -s 192.168.2.254 -p 50001 -f fw.log | \
    lr_log2report fw1_lea

=head1 BUGS / TO DO

This convertor needs perl's Date::Manip library.  It'd better use another
module for this, used by more Lire code.

The status and licensing of fw1-loggrabber is unknown.

=head1 SEE ALSO

Unfortunately, Check Point supplies no easily available documentation:
http://www.checkpoint.com/support/technical/documents/index.html is password
protected.  Some notes about the FireWall-1 product are on
http://www.checkpoint.com/products/protect/firewall-1.html .

=head1 AUTHORS

Torsten Fellhauer <torsten@fellhauer-web.de>

=head1 VERSION

$Id: fw1_lea2dlf.in,v 1.9 2006/07/23 13:16:35 vanbaal Exp $

=head1 COPYRIGHT

Copyright (C) 2003 Torsten Fellhauer

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program (see COPYING); if not, check with
http://www.gnu.org/copyleft/gpl.html.

=cut

# Local Variables:
# mode: cperl
# End:
