#############################################################################
# Copyright (c) 2020 Balabit
# Copyright (c) 2020 MileK <mileek@gmail.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation, 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; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
# As an additional exemption you are allowed to compile & link against the
# OpenSSL libraries as published by the OpenSSL project. See the file
# COPYING for details.
#
#############################################################################
#
#scl/paloalto/panos.conf -- Paloalto PAN-OS parser fro syslog-ng
#
#<12>Apr 14 16:48:54 paloalto.test.net 1,2020/04/14 16:48:54,unknown,SYSTEM,auth,0,2020/04/14 16:48:54,,auth-fail,,0,0,general,medium,failed authentication for user \'admin\'. Reason: Invalid username/password. From: 10.0.10.55.,1718,0x0,0,0,0,0,,paloalto
#<14>Apr 14 16:54:18 paloalto.test.net 1,2020/04/14 16:54:18,unknown,CONFIG,0,0,2020/04/14 16:54:18,10.0.10.55,,set,admin,Web,Succeeded, deviceconfig system,127,0x0,0,0,0,0,,paloalto

block parser panos-parser (prefix(".panos.") ...) {
  channel {
# Common fields - set dot-nv-pairs
    parser {
      csv-parser(
        columns("future_use1","receive_time","serial","type","subtype","future_use2","time_generated", "tmp")
        delimiters(',')
        prefix("`prefix`")
        flags(greedy)
	drop-invalid(yes)
        template("${LEGACY_MSGHDR}${MESSAGE}")
      );
    };
# Parse logs according to "type" field
    if (match('SYSTEM' value('`prefix`type') type(string))) {
      parser {

        # FUTURE_USE, Receive Time, Serial Number, Type, Content/Threat
        # Type, FUTURE_USE, Generated Time, Virtual System, Event ID,
        # Object, FUTURE_USE, FUTURE_USE, Module, Severity, Description,
        # Sequence Number, Action Flags, Device Group Hierarchy Level 1,
        # Device Group Hierarchy Level 2, Device Group Hierarchy Level 3,
        # Device Group Hierarchy Level 4, Virtual System Name, Device Name

        csv-parser(

          columns("vsys","eventid","object","future_use3","future_use4","module","severity","opaque","seqno","actionflags",
                  "dg_hier_level_1","dg_hier_level_2","dg_hier_level_3","dg_hier_level_4","vsys_name","device_name")

          prefix("`prefix`")
          template("${`prefix`tmp}")
          delimiters(',')
        );
      };
      filter { "${`prefix`device_name}" ne "" };
    } 
    elif (match('CONFIG' value('`prefix`type') type(string))) {

      # FUTURE_USE, Receive Time, Serial Number, Type, Subtype, FUTURE_USE,
      # Generated Time, Host, Virtual System, Command, Admin, Client,
      # Result, Configuration Path, Before Change Detail, After Change
      # Detail, Sequence Number, Action Flags, Device Group Hierarchy Level
      # 1, Device Group Hierarchy Level 2, Device Group Hierarchy Level 3,
      # Device Group Hierarchy Level 4, Virtual System Name, Device Name

      if {
          # non-custom format first
          parser {
            csv-parser(
              columns("host", "vsys","cmd","admin","client","result","path","seqno","actionflags",
                      "dg_hier_level_1","dg_hier_level_2","dg_hier_level_3","dg_hier_level_4","vsys_name","device_name")
              prefix("`prefix`")
              template("${`prefix`tmp}")
              delimiters(',')
              drop-invalid(yes)
            );
          };
      } else {
          # custom log with "before_change_detail" and "after_change_detail"
          # fields in the format
          parser {
            csv-parser(
              columns("host", "vsys","cmd","admin","client","result","path",
                      "before_change_detail","after_change_detail","seqno","actionflags",
                      "dg_hier_level_1","dg_hier_level_2","dg_hier_level_3","dg_hier_level_4","vsys_name","device_name")
              prefix("`prefix`")
              template("${`prefix`tmp}")
              delimiters(',')
            );
          };
         filter { "${`prefix`device_name}" ne "" };
      };
    } 
    elif (match('THREAT' value('`prefix`type') type(string))) {
      parser {
        csv-parser(
          # FUTURE_USE, Receive Time, Serial Number, Type, Threat/Content
          # Type, FUTURE_USE, Generated Time, Source Address, Destination
          # Address, NAT Source IP, NAT Destination IP, Rule Name, Source
          # User, Destination User, Application, Virtual System, Source
          # Zone, Destination Zone, Inbound Interface, Outbound Interface,
          # Log Action, FUTURE_USE, Session ID, Repeat Count, Source Port,
          # Destination Port, NAT Source Port, NAT Destination Port, Flags,
          # Protocol, Action, URL/Filename, Threat ID, Category, Severity,
          # Direction, Sequence Number, Action Flags, Source Location,
          # Destination Location, FUTURE_USE, Content Type, PCAP_ID, File
          # Digest, Cloud, URL Index, User Agent, File Type,
          # X-Forwarded-For, Referer, Sender, Subject, Recipient, Report ID,
          # Device Group Hierarchy Level 1, Device Group Hierarchy Level 2,
          # Device Group Hierarchy Level 3, Device Group Hierarchy Level 4,
          # Virtual System Name, Device Name, FUTURE_USE, Source VM UUID,
          # Destination VM UUID, HTTP Method, Tunnel ID/IMSI, Monitor
          # Tag/IMEI, Parent Session ID, Parent Start Time, Tunnel Type,
          # Threat Category, Content Version, FUTURE_USE, SCTP Association
          # ID, Payload Protocol ID, HTTP Headers, URL Category List, UUID
          # for rule, HTTP/2 Connection

          columns("src","dst","natsrc","natdst","rule","srcuser","dstuser","app","vsys","from","to",
                  "inbound_if","outbound_if","logset","future_use3","sessionid","repeatcnt",
                  "sport","dport","natsport","natdport","flags","proto","action","misc",
                  "threatid","category","severity","direction","seqno","actionflags",
                  "srcloc","dstloc","future_use4","contenttype","pcap_id","filedigest",
                  "cloud","url_idx","user_agent","filetype","xff","referer","sender","subject","recipient","reportid",
                  "dg_hier_level_1","dg_hier_level_2","dg_hier_level_3","dg_hier_level_4","vsys_name","device_name","future_use5",
                  "src_uuid","dst_uuid","http_method","tunnel_id/imsi","monitor_tag/imei",
                  "parent_session_id","parent_start_time","tunnel","thr_category","contentver",
                  "future_use6","assoc_id","ppid","http_headers","url_category_list",
                  "rule_uuid","http2_connection")
          prefix("`prefix`")
          template("${`prefix`tmp}")
          delimiters(',')
        );
      };

      # we require all columns up to device_name, the rest is optional
      filter { "${`prefix`device_name}" ne "" };
    } 
    elif (match('TRAFFIC' value('`prefix`type') type(string))) {
      parser {

        # FUTURE_USE, Receive Time, Serial Number, Type, Threat/Content
        # Type, FUTURE_USE, Generated Time, Source Address, Destination
        # Address, NAT Source IP, NAT Destination IP, Rule Name, Source
        # User, Destination User, Application, Virtual System, Source Zone,
        # Destination Zone, Inbound Interface, Outbound Interface, Log
        # Action, FUTURE_USE, Session ID, Repeat Count, Source Port,
        # Destination Port, NAT Source Port, NAT Destination Port, Flags,
        # Protocol, Action, Bytes, Bytes Sent, Bytes Received, Packets,
        # Start Time, Elapsed Time, Category, FUTURE_USE, Sequence Number,
        # Action Flags, Source Location, Destination Location, FUTURE_USE,
        # Packets Sent, Packets Received, Session End Reason, Device Group
        # Hierarchy Level 1, Device Group Hierarchy Level 2, Device Group
        # Hierarchy Level 3, Device Group Hierarchy Level 4, Virtual System
        # Name, Device Name, Action Source, Source VM UUID, Destination VM
        # UUID, Tunnel ID/IMSI, Monitor Tag/IMEI, Parent Session ID, Parent
        # Start Time, Tunnel Type, SCTP Association ID, SCTP Chunks, SCTP
        # Chunks Sent, SCTP Chunks Received, UUID for rule, HTTP/2
        # Connection

        csv-parser(
          columns("src","dst","natsrc","natdst","rule","srcuser","dstuser","app","vsys","from","to","inbound_if","outbound_if",
                  "logset","future_use3","sessionid","repeatcnt","sport","dport","natsport","natdport","flags","proto","action",
                  "bytes","bytes_sent","bytes_received","packets","start","sec","category","future_use4","seqno","actionflags",
                  "srcloc","dstloc","future_use5","pkts_sent","pkts_received","session_end_reason",
                  "dg_hier_level_1","dg_hier_level_2","dg_hier_level_3","dg_hier_level_4","vsys_name","device_name",
                  "action_source","src_uuid","dst_uuid","tunnel_id/imsi","monitortag/imei",
                  "parent_session_id","parent_start_time","tunnel","assoc_id",
                  "chunks","chunks_sent","chunks_received","rule_uuid",
                  "http2_connection","link_change_count",
                  "policy_id","link_switches",
                  "sdwan_cluster","sdwan_device_type","sdwan_cluster_type","sdwan_site","dynusergroup_name")
          prefix("`prefix`")
          template("${`prefix`tmp}")
          delimiters(',')
        );
      };
      # we require all columns up to device_name, the rest is optional
      filter { "${`prefix`device_name}" ne "" };
    } 
    elif (match('HIP-MATCH' value('`prefix`type') type(string))) {
      parser {

        # FUTURE_USE, Receive Time, Serial Number, Type, Threat/Content
        # Type, FUTURE_USE, Generated Time, Source User, Virtual System,
        # Machine name, OS, Source Address, HIP, Repeat Count, HIP Type,
        # FUTURE_USE, FUTURE_USE, Sequence Number, Action Flags, Device
        # Group Hierarchy Level 1, Device Group Hierarchy Level 2, Device
        # Group Hierarchy Level 3, Device Group Hierarchy Level 4, Virtual
        # System Name, Device Name, Virtual System ID, IPv6 Source Address,
        # Host ID, User Device Serial Number

        csv-parser(
          columns("srcuser","vsys","machine_name","os","src","matchname","repeatcnt","matchtype","future_use3","future_use4","seqno","actionflags",
                  "dg_hier_level_1","dg_hier_level_2","dg_hier_level_3","dg_hier_level_4","vsys_name","device_name",
                  "vsys_id","srcipv6","hostid","serialnumber")
          prefix("`prefix`")
          template("${`prefix`tmp}")
          delimiters(',')
        );
      };
      filter { "${`prefix`device_name}" ne "" };
    } 
    elif (match('CORRELATION' value('`prefix`type') type(string))) {
      parser {

        # FUTURE_USE, Receive Time, Serial Number, Type, Content/Threat
        # Type, FUTURE_USE, Generated Time, Source Address.  Source User,
        # Virtual System, Category, Severity, Device Group Hierarchy Level
        # 1, Device Group Hierarchy Level 2, Device Group Hierarchy Level 3,
        # Device Group Hierarchy Level 4, Virtual System Name, Device Name,
        # Virtual System ID, Object Name, Object ID, Evidence

        csv-parser(
          columns("src","srcuser","vsys","category","severity",
                  "dg_hier_level_1","dg_hier_level_2","dg_hier_level_3","dg_hier_level_4","vsys_name","device_name",
                  "vsys_id","objectname","object_id","evidence")
          prefix("`prefix`")
          template("${`prefix`tmp}")
          delimiters(',')
        );
      };
      filter { "${`prefix`device_name}" ne "" };
    } 
    elif (match('USERID' value('`prefix`type') type(string))) {
      parser {
        csv-parser(

          # FUTURE_USE, Receive Time, Serial Number, Type, Threat/Content
          # Type, FUTURE_USE, Generated Time, Virtual System, Source IP,
          # User, Data Source Name, Event ID, Repeat Count, Time Out
          # Threshold, Source Port, Destination Port, Data Source, Data
          # Source Type, Sequence Number, Action Flags, Device Group
          # Hierarchy Level 1, Device Group Hierarchy Level 2, Device Group
          # Hierarchy Level 3, Device Group Hierarchy Level 4, Virtual
          # System Name, Device Name, Virtual System ID, Factor Type, Factor
          # Completion Time, Factor Number, FUTURE_USE, FUTURE_USE, User
          # Group Flags, User by Source

          columns("vsys","ip","user","datasourcename","eventid","repeatcnt","timeout","beginport","endport","datasource","datasourcetype","seqno","actionflags",
                  "dg_hier_level_1","dg_hier_level_2","dg_hier_level_3","dg_hier_level_4","vsys_name","device_name",
                  "vsys_id","factortype","factorcompletiontime","factorno","future_use3","future_use4","ugflags","userbysource")
          prefix("`prefix`")
          template("${`prefix`tmp}")
          delimiters(',')
        );
      };
      filter { "${`prefix`device_name}" ne "" };
    };

    # no else branch, so we would drop messages where type does not match
    # any of the known ones in the branches above

    rewrite {

      # fix the message in case we forward it to a syslog consumer
      set("${LEGACY_MSGHDR}${MESSAGE}" value("MESSAGE"));
      set("paloalto_panos" value("PROGRAM"));
      unset(value("`prefix`tmp"));
    };

  };
};
application panos[syslog] {
    filter { match("1," value("PROGRAM") type(string) flags(prefix)); };
    parser { panos-parser(); };
};
