/* pmpanngfw.c * * this detects logs sent by Palo Alto Networks NGFW and transforms CSV into tab-separated fields * for handling inside the mmnormalize * * Example: foo,"bar,""baz""",qux becomes foobar,"baz"qux * * created 2010-12-13 by Luigi Mori (lmori@paloaltonetworks.com) based on pmsnare * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * -or- * see COPYING.ASL20 in the source distribution * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "config.h" #include "rsyslog.h" #include #include #include #include #include "conf.h" #include "syslogd-types.h" #include "template.h" #include "msg.h" #include "module-template.h" #include "glbl.h" #include "errmsg.h" #include "parser.h" #include "datetime.h" #include "unicode-helper.h" #include "typedefs.h" #include "rsconf.h" MODULE_TYPE_PARSER MODULE_TYPE_NOKEEP PARSER_NAME("rsyslog.panngfw") /* internal structures */ DEF_PMOD_STATIC_DATA DEFobjCurrIf(glbl) DEFobjCurrIf(parser) DEFobjCurrIf(datetime) /* static data */ static int bParseHOSTNAMEandTAG; /* cache for the equally-named global param - performance enhancement */ typedef struct { uint64 value; uint64 mask; } log_type_t; const log_type_t log_types[] = { { 0x002c544145524854ULL, 0x00FFFFFFFFFFFFFFULL }, /* THREAT, */ { 0x2c43494646415254ULL, 0xFFFFFFFFFFFFFFFFULL }, /* TRAFFIC, */ { 0x002c4d4554535953ULL, 0x00FFFFFFFFFFFFFFULL }, /* CONFIG */ { 0x002c4749464e4f43ULL, 0x00FFFFFFFFFFFFFFULL } /* SYSTEM */ }; #define NUM_LOG_TYPES (sizeof(log_types)/sizeof(log_type_t)) BEGINisCompatibleWithFeature CODESTARTisCompatibleWithFeature if(eFeat == sFEATUREAutomaticSanitazion) iRet = RS_RET_OK; if(eFeat == sFEATUREAutomaticPRIParsing) iRet = RS_RET_OK; ENDisCompatibleWithFeature BEGINparse uchar *p2parse; uchar *p2target; uchar *msgend; int lenMsg, lenDelta; int state; int num_fields = 4; uchar *f3_commas[3]; int cur_comma = 0; uint64 log_type; unsigned int j; CODESTARTparse #define CSV_DELIMITER '\t' #define STATE_FIELD_START 0 #define STATE_IN_FIELD 1 #define STATE_IN_QUOTE 2 #define STATE_IN_QUOTE_QUOTE 3 state = STATE_FIELD_START; dbgprintf("Message will now be parsed by fix Palo Alto Networks NGFW parser.\n"); assert(pMsg != NULL); assert(pMsg->pszRawMsg != NULL); lenMsg = pMsg->iLenRawMsg - pMsg->offAfterPRI; /* note: offAfterPRI is already the number of PRI chars (do not add one!) */ p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */ msgend = p2parse+lenMsg; dbgprintf("pmpanngfw: msg to look at: [%d]'%s'\n", lenMsg, p2parse); /* pass the first 3 fields */ while(p2parse < msgend) { if (*p2parse == ',') { f3_commas[cur_comma] = p2parse; if (cur_comma == 2) { break; } cur_comma++; } p2parse++; } /* check number of fields detected so far */ if (cur_comma != 2) { dbgprintf("not a PAN-OS syslog message: first 3 fields not found\n"); ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE); } /* check msg length */ p2parse++; if ((p2parse > msgend) || ((msgend - p2parse) < (int)sizeof(uint64))) { dbgprintf("not a PAN-OS syslog message: too short\n"); ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE); } /* check log type */ log_type = *((uint64 *)p2parse); for(j = 0; j < (int)NUM_LOG_TYPES; j++) { if ((log_type & log_types[j].mask) == log_types[j].value) break; } if (j == NUM_LOG_TYPES) { dbgprintf("not a PAN-OS syslog message, log type: %llx\n", log_type); ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE); } /* set the delimiter */ *f3_commas[0] = CSV_DELIMITER; *f3_commas[1] = CSV_DELIMITER; *f3_commas[2] = CSV_DELIMITER; p2target = p2parse; while(p2parse < msgend) { switch(state) { case STATE_FIELD_START: switch(*p2parse) { case '"': state = STATE_IN_QUOTE; p2parse++; break; case ',': state = STATE_FIELD_START; *p2target = CSV_DELIMITER; num_fields++; p2parse++; p2target++; break; default: state = STATE_IN_FIELD; *p2target = *p2parse; p2parse++; p2target++; } break; case STATE_IN_FIELD: switch(*p2parse) { case ',': state = STATE_FIELD_START; *p2target = CSV_DELIMITER; num_fields++; p2parse++; p2target++; break; default: *p2target = *p2parse; p2parse++; p2target++; } break; case STATE_IN_QUOTE: switch(*p2parse) { case '"': state = STATE_IN_QUOTE_QUOTE; p2parse++; break; default: *p2target = *p2parse; p2parse++; p2target++; } break; case STATE_IN_QUOTE_QUOTE: switch(*p2parse) { case '"': state = STATE_IN_QUOTE; *p2target = *p2parse; p2parse++; p2target++; break; case ',': state = STATE_FIELD_START; *p2target = CSV_DELIMITER; num_fields++; p2parse++; p2target++; break; default: dbgprintf("pmpanngfw: martian char (%d) after quote in quote\n", *p2parse); ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE); } break; default: dbgprintf("how could I have reached this state ?!?\n"); ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE); } } if(p2parse != p2target) { lenDelta = p2parse - p2target; assert(lenDelta >= 2); *p2target = 0; pMsg->iLenRawMsg -= lenDelta; pMsg->iLenMSG -= lenDelta; } lenMsg = p2target - (pMsg->pszRawMsg + pMsg->offAfterPRI); DBGPRINTF("pmpanngfw: new message: [%d]'%s'\n", lenMsg, pMsg->pszRawMsg + pMsg->offAfterPRI); DBGPRINTF("pmpanngfw: # fields: %d\n", num_fields); ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE); finalize_it: ENDparse BEGINmodExit CODESTARTmodExit /* release what we no longer need */ objRelease(glbl, CORE_COMPONENT); objRelease(parser, CORE_COMPONENT); objRelease(datetime, CORE_COMPONENT); ENDmodExit BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_PMOD_QUERIES CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES ENDqueryEtryPt BEGINmodInit() CODESTARTmodInit *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(glbl, CORE_COMPONENT)); CHKiRet(objUse(parser, CORE_COMPONENT)); CHKiRet(objUse(datetime, CORE_COMPONENT)); DBGPRINTF("panngfw parser init called, compiled with version %s\n", VERSION); bParseHOSTNAMEandTAG = glbl.GetParseHOSTNAMEandTAG(loadConf); /* cache value, is set only during rsyslogd option processing */ ENDmodInit /* vim:set ai: */