diff options
Diffstat (limited to 'framework/src/suricata/src/app-layer-parser.c')
-rw-r--r-- | framework/src/suricata/src/app-layer-parser.c | 1385 |
1 files changed, 1385 insertions, 0 deletions
diff --git a/framework/src/suricata/src/app-layer-parser.c b/framework/src/suricata/src/app-layer-parser.c new file mode 100644 index 00000000..134f9909 --- /dev/null +++ b/framework/src/suricata/src/app-layer-parser.c @@ -0,0 +1,1385 @@ +/* Copyright (C) 2007-2013 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * 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 + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Victor Julien <victor@inliniac.net> + * + * Generic App-layer parsing functions. + */ + +#include "suricata-common.h" +#include "debug.h" +#include "util-unittest.h" +#include "decode.h" +#include "threads.h" + +#include "util-print.h" +#include "util-pool.h" + +#include "flow-util.h" +#include "flow-private.h" + +#include "detect-engine-state.h" +#include "detect-engine-port.h" + +#include "stream-tcp.h" +#include "stream-tcp-private.h" +#include "stream.h" +#include "stream-tcp-reassemble.h" + +#include "app-layer.h" +#include "app-layer-protos.h" +#include "app-layer-parser.h" +#include "app-layer-smb.h" +#include "app-layer-smb2.h" +#include "app-layer-dcerpc.h" +#include "app-layer-dcerpc-udp.h" +#include "app-layer-htp.h" +#include "app-layer-ftp.h" +#include "app-layer-ssl.h" +#include "app-layer-ssh.h" +#include "app-layer-smtp.h" +#include "app-layer-dns-udp.h" +#include "app-layer-dns-tcp.h" +#include "app-layer-modbus.h" + +#include "conf.h" +#include "util-spm.h" + +#include "util-debug.h" +#include "decode-events.h" +#include "util-unittest-helper.h" +#include "util-validate.h" + +#include "runmodes.h" + +static GetActiveTxIdFunc AppLayerGetActiveTxIdFuncPtr = NULL; + +struct AppLayerParserThreadCtx_ { + void *alproto_local_storage[FLOW_PROTO_MAX][ALPROTO_MAX]; +}; + + +/** + * \brief App layer protocol parser context. + */ +typedef struct AppLayerParserProtoCtx_ +{ + /* 0 - to_server, 1 - to_client. */ + int (*Parser[2])(Flow *f, void *protocol_state, + AppLayerParserState *pstate, + uint8_t *input, uint32_t input_len, + void *local_storage); + char logger; + + void *(*StateAlloc)(void); + void (*StateFree)(void *); + void (*StateTransactionFree)(void *, uint64_t); + void *(*LocalStorageAlloc)(void); + void (*LocalStorageFree)(void *); + + void (*Truncate)(void *, uint8_t); + FileContainer *(*StateGetFiles)(void *, uint8_t); + AppLayerDecoderEvents *(*StateGetEvents)(void *, uint64_t); + int (*StateHasEvents)(void *); + + int (*StateGetProgress)(void *alstate, uint8_t direction); + uint64_t (*StateGetTxCnt)(void *alstate); + void *(*StateGetTx)(void *alstate, uint64_t tx_id); + int (*StateGetProgressCompletionStatus)(uint8_t direction); + int (*StateGetEventInfo)(const char *event_name, + int *event_id, AppLayerEventType *event_type); + + int (*StateHasTxDetectState)(void *alstate); + DetectEngineState *(*GetTxDetectState)(void *tx); + int (*SetTxDetectState)(void *alstate, void *tx, DetectEngineState *); + + /* Indicates the direction the parser is ready to see the data + * the first time for a flow. Values accepted - + * STREAM_TOSERVER, STREAM_TOCLIENT */ + uint8_t first_data_dir; + +#ifdef UNITTESTS + void (*RegisterUnittests)(void); +#endif +} AppLayerParserProtoCtx; + +typedef struct AppLayerParserCtx_ { + AppLayerParserProtoCtx ctxs[FLOW_PROTO_MAX][ALPROTO_MAX]; +} AppLayerParserCtx; + +struct AppLayerParserState_ { + uint8_t flags; + + /* State version, incremented for each update. Can wrap around. */ + uint8_t version; + /* Indicates the current transaction that is being inspected. + * We have a var per direction. */ + uint64_t inspect_id[2]; + /* Indicates the current transaction being logged. Unlike inspect_id, + * we don't need a var per direction since we don't log a transaction + * unless we have the entire transaction. */ + uint64_t log_id; + + /* Used to store decoder events. */ + AppLayerDecoderEvents *decoder_events; +}; + +/* Static global version of the parser context. + * Post 2.0 let's look at changing this to move it out to app-layer.c. */ +static AppLayerParserCtx alp_ctx; + +AppLayerParserState *AppLayerParserStateAlloc(void) +{ + SCEnter(); + + AppLayerParserState *pstate = (AppLayerParserState *)SCMalloc(sizeof(*pstate)); + if (pstate == NULL) + goto end; + memset(pstate, 0, sizeof(*pstate)); + + end: + SCReturnPtr(pstate, "AppLayerParserState"); +} + +void AppLayerParserStateFree(AppLayerParserState *pstate) +{ + SCEnter(); + + if (pstate->decoder_events != NULL) + AppLayerDecoderEventsFreeEvents(&pstate->decoder_events); + SCFree(pstate); + + SCReturn; +} + +int AppLayerParserSetup(void) +{ + SCEnter(); + + memset(&alp_ctx, 0, sizeof(alp_ctx)); + + /* set the default tx handler if none was set explicitly */ + if (AppLayerGetActiveTxIdFuncPtr == NULL) { + RegisterAppLayerGetActiveTxIdFunc(AppLayerTransactionGetActiveDetectLog); + } + + SCReturnInt(0); +} + +int AppLayerParserDeSetup(void) +{ + SCEnter(); + + SCReturnInt(0); +} + +AppLayerParserThreadCtx *AppLayerParserThreadCtxAlloc(void) +{ + SCEnter(); + + AppProto alproto = 0; + int flow_proto = 0; + AppLayerParserThreadCtx *tctx; + + tctx = SCMalloc(sizeof(*tctx)); + if (tctx == NULL) + goto end; + memset(tctx, 0, sizeof(*tctx)); + + for (flow_proto = 0; flow_proto < FLOW_PROTO_DEFAULT; flow_proto++) { + for (alproto = 0; alproto < ALPROTO_MAX; alproto++) { + uint8_t ipproto = FlowGetReverseProtoMapping(flow_proto); + + tctx->alproto_local_storage[flow_proto][alproto] = + AppLayerParserGetProtocolParserLocalStorage(ipproto, alproto); + } + } + + end: + SCReturnPtr(tctx, "void *"); +} + +void AppLayerParserThreadCtxFree(AppLayerParserThreadCtx *tctx) +{ + SCEnter(); + + AppProto alproto = 0; + int flow_proto = 0; + + for (flow_proto = 0; flow_proto < FLOW_PROTO_DEFAULT; flow_proto++) { + for (alproto = 0; alproto < ALPROTO_MAX; alproto++) { + uint8_t ipproto = FlowGetReverseProtoMapping(flow_proto); + + AppLayerParserDestroyProtocolParserLocalStorage(ipproto, alproto, + tctx->alproto_local_storage[flow_proto][alproto]); + } + } + + SCFree(tctx); + SCReturn; +} + +int AppLayerParserConfParserEnabled(const char *ipproto, + const char *alproto_name) +{ + SCEnter(); + + int enabled = 1; + char param[100]; + ConfNode *node; + int r; + + if (RunmodeIsUnittests()) + goto enabled; + + r = snprintf(param, sizeof(param), "%s%s%s", "app-layer.protocols.", + alproto_name, ".enabled"); + if (r < 0) { + SCLogError(SC_ERR_FATAL, "snprintf failure."); + exit(EXIT_FAILURE); + } else if (r > (int)sizeof(param)) { + SCLogError(SC_ERR_FATAL, "buffer not big enough to write param."); + exit(EXIT_FAILURE); + } + + node = ConfGetNode(param); + if (node == NULL) { + SCLogDebug("Entry for %s not found.", param); + r = snprintf(param, sizeof(param), "%s%s%s%s%s", "app-layer.protocols.", + alproto_name, ".", ipproto, ".enabled"); + if (r < 0) { + SCLogError(SC_ERR_FATAL, "snprintf failure."); + exit(EXIT_FAILURE); + } else if (r > (int)sizeof(param)) { + SCLogError(SC_ERR_FATAL, "buffer not big enough to write param."); + exit(EXIT_FAILURE); + } + + node = ConfGetNode(param); + if (node == NULL) { + SCLogDebug("Entry for %s not found.", param); + goto enabled; + } + } + + if (strcasecmp(node->val, "yes") == 0) { + goto enabled; + } else if (strcasecmp(node->val, "no") == 0) { + goto disabled; + } else if (strcasecmp(node->val, "detection-only") == 0) { + goto disabled; + } else { + SCLogError(SC_ERR_FATAL, "Invalid value found for %s.", param); + exit(EXIT_FAILURE); + } + + disabled: + enabled = 0; + enabled: + SCReturnInt(enabled); +} + +/***** Parser related registration *****/ + +int AppLayerParserRegisterParser(uint8_t ipproto, AppProto alproto, + uint8_t direction, + int (*Parser)(Flow *f, void *protocol_state, + AppLayerParserState *pstate, + uint8_t *buf, uint32_t buf_len, + void *local_storage)) +{ + SCEnter(); + + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + Parser[(direction & STREAM_TOSERVER) ? 0 : 1] = Parser; + + SCReturnInt(0); +} + +void AppLayerParserRegisterParserAcceptableDataDirection(uint8_t ipproto, AppProto alproto, + uint8_t direction) +{ + SCEnter(); + + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].first_data_dir |= + (direction & (STREAM_TOSERVER | STREAM_TOCLIENT)); + + SCReturn; +} + +void AppLayerParserRegisterStateFuncs(uint8_t ipproto, AppProto alproto, + void *(*StateAlloc)(void), + void (*StateFree)(void *)) +{ + SCEnter(); + + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateAlloc = + StateAlloc; + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateFree = + StateFree; + + SCReturn; +} + +void AppLayerParserRegisterLocalStorageFunc(uint8_t ipproto, AppProto alproto, + void *(*LocalStorageAlloc)(void), + void (*LocalStorageFree)(void *)) +{ + SCEnter(); + + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].LocalStorageAlloc = + LocalStorageAlloc; + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].LocalStorageFree = + LocalStorageFree; + + SCReturn; +} + +void AppLayerParserRegisterGetFilesFunc(uint8_t ipproto, AppProto alproto, + FileContainer *(*StateGetFiles)(void *, uint8_t)) +{ + SCEnter(); + + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateGetFiles = + StateGetFiles; + + SCReturn; +} + +void AppLayerParserRegisterGetEventsFunc(uint8_t ipproto, AppProto alproto, + AppLayerDecoderEvents *(*StateGetEvents)(void *, uint64_t)) +{ + SCEnter(); + + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateGetEvents = + StateGetEvents; + + SCReturn; +} + +void AppLayerParserRegisterHasEventsFunc(uint8_t ipproto, AppProto alproto, + int (*StateHasEvents)(void *)) +{ + SCEnter(); + + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateHasEvents = + StateHasEvents; + + SCReturn; +} + +void AppLayerParserRegisterLogger(uint8_t ipproto, AppProto alproto) +{ + SCEnter(); + + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].logger = TRUE; + + SCReturn; +} + +void AppLayerParserRegisterTruncateFunc(uint8_t ipproto, AppProto alproto, + void (*Truncate)(void *, uint8_t)) +{ + SCEnter(); + + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].Truncate = Truncate; + + SCReturn; +} + +void AppLayerParserRegisterGetStateProgressFunc(uint8_t ipproto, AppProto alproto, + int (*StateGetProgress)(void *alstate, uint8_t direction)) +{ + SCEnter(); + + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + StateGetProgress = StateGetProgress; + + SCReturn; +} + +void AppLayerParserRegisterTxFreeFunc(uint8_t ipproto, AppProto alproto, + void (*StateTransactionFree)(void *, uint64_t)) +{ + SCEnter(); + + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + StateTransactionFree = StateTransactionFree; + + SCReturn; +} + +void AppLayerParserRegisterGetTxCnt(uint8_t ipproto, AppProto alproto, + uint64_t (*StateGetTxCnt)(void *alstate)) +{ + SCEnter(); + + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + StateGetTxCnt = StateGetTxCnt; + + SCReturn; +} + +void AppLayerParserRegisterGetTx(uint8_t ipproto, AppProto alproto, + void *(StateGetTx)(void *alstate, uint64_t tx_id)) +{ + SCEnter(); + + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + StateGetTx = StateGetTx; + + SCReturn; +} + +void AppLayerParserRegisterGetStateProgressCompletionStatus(uint8_t ipproto, + AppProto alproto, + int (*StateGetProgressCompletionStatus)(uint8_t direction)) +{ + SCEnter(); + + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + StateGetProgressCompletionStatus = StateGetProgressCompletionStatus; + + SCReturn; +} + +void AppLayerParserRegisterGetEventInfo(uint8_t ipproto, AppProto alproto, + int (*StateGetEventInfo)(const char *event_name, int *event_id, + AppLayerEventType *event_type)) +{ + SCEnter(); + + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + StateGetEventInfo = StateGetEventInfo; + + SCReturn; +} + +void AppLayerParserRegisterDetectStateFuncs(uint8_t ipproto, AppProto alproto, + int (*StateHasTxDetectState)(void *alstate), + DetectEngineState *(*GetTxDetectState)(void *tx), + int (*SetTxDetectState)(void *alstate, void *tx, DetectEngineState *)) +{ + SCEnter(); + + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateHasTxDetectState = StateHasTxDetectState; + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetTxDetectState = GetTxDetectState; + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].SetTxDetectState = SetTxDetectState; + + SCReturn; +} + +/***** Get and transaction functions *****/ + +void *AppLayerParserGetProtocolParserLocalStorage(uint8_t ipproto, AppProto alproto) +{ + SCEnter(); + void * r = NULL; + + if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + LocalStorageAlloc != NULL) + { + r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + LocalStorageAlloc(); + } + + SCReturnPtr(r, "void *"); +} + +void AppLayerParserDestroyProtocolParserLocalStorage(uint8_t ipproto, AppProto alproto, + void *local_data) +{ + SCEnter(); + + if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + LocalStorageFree != NULL) + { + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + LocalStorageFree(local_data); + } + + SCReturn; +} + +uint64_t AppLayerParserGetTransactionLogId(AppLayerParserState *pstate) +{ + SCEnter(); + + SCReturnCT((pstate == NULL) ? 0 : pstate->log_id, "uint64_t"); +} + +void AppLayerParserSetTransactionLogId(AppLayerParserState *pstate) +{ + SCEnter(); + + if (pstate != NULL) + pstate->log_id++; + + SCReturn; +} + +uint64_t AppLayerParserGetTransactionInspectId(AppLayerParserState *pstate, uint8_t direction) +{ + SCEnter(); + + if (pstate == NULL) + SCReturnCT(0ULL, "uint64_t"); + + SCReturnCT(pstate->inspect_id[direction & STREAM_TOSERVER ? 0 : 1], "uint64_t"); +} + +void AppLayerParserSetTransactionInspectId(AppLayerParserState *pstate, + const uint8_t ipproto, const AppProto alproto, + void *alstate, const uint8_t flags) +{ + SCEnter(); + + int direction = (flags & STREAM_TOSERVER) ? 0 : 1; + uint64_t total_txs = AppLayerParserGetTxCnt(ipproto, alproto, alstate); + uint64_t idx = AppLayerParserGetTransactionInspectId(pstate, flags); + int state_done_progress = AppLayerParserGetStateProgressCompletionStatus(ipproto, alproto, flags); + void *tx; + int state_progress; + + for (; idx < total_txs; idx++) { + tx = AppLayerParserGetTx(ipproto, alproto, alstate, idx); + if (tx == NULL) + continue; + state_progress = AppLayerParserGetStateProgress(ipproto, alproto, tx, flags); + if (state_progress >= state_done_progress) + continue; + else + break; + } + pstate->inspect_id[direction] = idx; + + SCReturn; +} + +AppLayerDecoderEvents *AppLayerParserGetDecoderEvents(AppLayerParserState *pstate) +{ + SCEnter(); + + SCReturnPtr(pstate->decoder_events, + "AppLayerDecoderEvents *"); +} + +void AppLayerParserSetDecoderEvents(AppLayerParserState *pstate, AppLayerDecoderEvents *devents) +{ + pstate->decoder_events = devents; +} + +AppLayerDecoderEvents *AppLayerParserGetEventsByTx(uint8_t ipproto, AppProto alproto, + void *alstate, uint64_t tx_id) +{ + SCEnter(); + + AppLayerDecoderEvents *ptr = NULL; + + if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + StateGetEvents != NULL) + { + ptr = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + StateGetEvents(alstate, tx_id); + } + + SCReturnPtr(ptr, "AppLayerDecoderEvents *"); +} + +uint16_t AppLayerParserGetStateVersion(AppLayerParserState *pstate) +{ + SCEnter(); + SCReturnCT((pstate == NULL) ? 0 : pstate->version, "uint8_t"); +} + +FileContainer *AppLayerParserGetFiles(uint8_t ipproto, AppProto alproto, + void *alstate, uint8_t direction) +{ + SCEnter(); + + FileContainer *ptr = NULL; + + if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + StateGetFiles != NULL) + { + ptr = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + StateGetFiles(alstate, direction); + } + + SCReturnPtr(ptr, "FileContainer *"); +} + +/** \brief active TX retrieval for normal ops: so with detection and logging + * + * \retval tx_id lowest tx_id that still needs work */ +uint64_t AppLayerTransactionGetActiveDetectLog(Flow *f, uint8_t flags) +{ + AppLayerParserProtoCtx *p = &alp_ctx.ctxs[FlowGetProtoMapping(f->proto)][f->alproto]; + uint64_t log_id = f->alparser->log_id; + uint64_t inspect_id = f->alparser->inspect_id[flags & STREAM_TOSERVER ? 0 : 1]; + if (p->logger == TRUE) { + return (log_id < inspect_id) ? log_id : inspect_id; + } else { + return inspect_id; + } +} + +/** \brief active TX retrieval for logging only: so NO detection + * + * If the logger is enabled, we simply return the log_id here. + * + * Otherwise, we go look for the tx id. There probably is no point + * in running this function in that case though. With no detection + * and no logging, why run a parser in the first place? + **/ +uint64_t AppLayerTransactionGetActiveLogOnly(Flow *f, uint8_t flags) +{ + AppLayerParserProtoCtx *p = &alp_ctx.ctxs[f->protomap][f->alproto]; + + if (p->logger == TRUE) { + uint64_t log_id = f->alparser->log_id; + SCLogDebug("returning %"PRIu64, log_id); + return log_id; + } + + /* logger is disabled, return highest 'complete' tx id */ + uint64_t total_txs = AppLayerParserGetTxCnt(f->proto, f->alproto, f->alstate); + uint64_t idx = AppLayerParserGetTransactionInspectId(f->alparser, flags); + int state_done_progress = AppLayerParserGetStateProgressCompletionStatus(f->proto, f->alproto, flags); + void *tx; + int state_progress; + + for (; idx < total_txs; idx++) { + tx = AppLayerParserGetTx(f->proto, f->alproto, f->alstate, idx); + if (tx == NULL) + continue; + state_progress = AppLayerParserGetStateProgress(f->proto, f->alproto, tx, flags); + if (state_progress >= state_done_progress) + continue; + else + break; + } + SCLogDebug("returning %"PRIu64, idx); + return idx; +} + +void RegisterAppLayerGetActiveTxIdFunc(GetActiveTxIdFunc FuncPtr) +{ + //BUG_ON(AppLayerGetActiveTxIdFuncPtr != NULL); + AppLayerGetActiveTxIdFuncPtr = FuncPtr; + SCLogDebug("AppLayerGetActiveTxIdFuncPtr is now %p", AppLayerGetActiveTxIdFuncPtr); +} + +/** + * \brief Get 'active' tx id, meaning the lowest id that still need work. + * + * \retval id tx id + */ +static uint64_t AppLayerTransactionGetActive(Flow *f, uint8_t flags) +{ + BUG_ON(AppLayerGetActiveTxIdFuncPtr == NULL); + + return AppLayerGetActiveTxIdFuncPtr(f, flags); +} + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +/** + * \brief remove obsolete (inspected and logged) transactions + */ +static void AppLayerParserTransactionsCleanup(Flow *f) +{ + DEBUG_ASSERT_FLOW_LOCKED(f); + + AppLayerParserProtoCtx *p = &alp_ctx.ctxs[FlowGetProtoMapping(f->proto)][f->alproto]; + if (p->StateTransactionFree == NULL) + return; + + uint64_t tx_id_ts = AppLayerTransactionGetActive(f, STREAM_TOSERVER); + uint64_t tx_id_tc = AppLayerTransactionGetActive(f, STREAM_TOCLIENT); + + uint64_t min = MIN(tx_id_ts, tx_id_tc); + if (min > 0) { + SCLogDebug("freeing %"PRIu64" %p", min - 1, p->StateTransactionFree); + p->StateTransactionFree(f->alstate, min - 1); + } +} + +#define IS_DISRUPTED(flags) \ + ((flags) & (STREAM_DEPTH|STREAM_GAP)) + +/** + * \brief get the progress value for a tx/protocol + * + * If the stream is disrupted, we return the 'completion' value. + */ +int AppLayerParserGetStateProgress(uint8_t ipproto, AppProto alproto, + void *alstate, uint8_t flags) +{ + SCEnter(); + int r = 0; + if (unlikely(IS_DISRUPTED(flags))) { + r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + StateGetProgressCompletionStatus(flags); + } else { + r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + StateGetProgress(alstate, flags); + } + SCReturnInt(r); +} + +uint64_t AppLayerParserGetTxCnt(uint8_t ipproto, AppProto alproto, void *alstate) +{ + SCEnter(); + uint64_t r = 0; + r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + StateGetTxCnt(alstate); + SCReturnCT(r, "uint64_t"); +} + +void *AppLayerParserGetTx(uint8_t ipproto, AppProto alproto, void *alstate, uint64_t tx_id) +{ + SCEnter(); + void * r = NULL; + r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + StateGetTx(alstate, tx_id); + SCReturnPtr(r, "void *"); +} + +int AppLayerParserGetStateProgressCompletionStatus(uint8_t ipproto, AppProto alproto, + uint8_t direction) +{ + SCEnter(); + int r = 0; + r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + StateGetProgressCompletionStatus(direction); + SCReturnInt(r); +} + +int AppLayerParserGetEventInfo(uint8_t ipproto, AppProto alproto, const char *event_name, + int *event_id, AppLayerEventType *event_type) +{ + SCEnter(); + int ipproto_map = FlowGetProtoMapping(ipproto); + int r = (alp_ctx.ctxs[ipproto_map][alproto].StateGetEventInfo == NULL) ? + -1 : alp_ctx.ctxs[ipproto_map][alproto].StateGetEventInfo(event_name, event_id, event_type); + SCReturnInt(r); +} + +uint8_t AppLayerParserGetFirstDataDir(uint8_t ipproto, AppProto alproto) +{ + SCEnter(); + uint8_t r = 0; + r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + first_data_dir; + SCReturnCT(r, "uint8_t"); +} + +uint64_t AppLayerParserGetTransactionActive(uint8_t ipproto, AppProto alproto, + AppLayerParserState *pstate, uint8_t direction) +{ + SCEnter(); + + uint64_t active_id; + + uint64_t log_id = pstate->log_id; + uint64_t inspect_id = pstate->inspect_id[direction & STREAM_TOSERVER ? 0 : 1]; + if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].logger == TRUE) { + active_id = (log_id < inspect_id) ? log_id : inspect_id; + } else { + active_id = inspect_id; + } + + SCReturnCT(active_id, "uint64_t"); +} + +int AppLayerParserSupportsTxDetectState(uint8_t ipproto, AppProto alproto) +{ + if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetTxDetectState != NULL) + return TRUE; + return FALSE; +} + +int AppLayerParserHasTxDetectState(uint8_t ipproto, AppProto alproto, void *alstate) +{ + int r; + SCEnter(); + if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateHasTxDetectState == NULL) + return -ENOSYS; + r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateHasTxDetectState(alstate); + SCReturnInt(r); +} + +DetectEngineState *AppLayerParserGetTxDetectState(uint8_t ipproto, AppProto alproto, void *tx) +{ + SCEnter(); + DetectEngineState *s; + s = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetTxDetectState(tx); + SCReturnPtr(s, "DetectEngineState"); +} + +int AppLayerParserSetTxDetectState(uint8_t ipproto, AppProto alproto, + void *alstate, void *tx, DetectEngineState *s) +{ + int r; + SCEnter(); + if ((alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].GetTxDetectState(tx) != NULL)) + SCReturnInt(-EBUSY); + r = alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].SetTxDetectState(alstate, tx, s); + SCReturnInt(r); +} + +/***** General *****/ + +int AppLayerParserParse(AppLayerParserThreadCtx *alp_tctx, Flow *f, AppProto alproto, + uint8_t flags, uint8_t *input, uint32_t input_len) +{ + SCEnter(); +#ifdef DEBUG_VALIDATION + BUG_ON(f->protomap != FlowGetProtoMapping(f->proto)); +#endif + AppLayerParserState *pstate = NULL; + AppLayerParserProtoCtx *p = &alp_ctx.ctxs[f->protomap][alproto]; + void *alstate = NULL; + + /* we don't have the parser registered for this protocol */ + if (p->StateAlloc == NULL) + goto end; + + /* Do this check before calling AppLayerParse */ + if (flags & STREAM_GAP) { + SCLogDebug("stream gap detected (missing packets), " + "this is not yet supported."); + + if (f->alstate != NULL) + AppLayerParserStreamTruncated(f->proto, alproto, f->alstate, flags); + goto error; + } + + /* Get the parser state (if any) */ + pstate = f->alparser; + if (pstate == NULL) { + f->alparser = pstate = AppLayerParserStateAlloc(); + if (pstate == NULL) + goto error; + } + pstate->version++; + SCLogDebug("app layer parser state version incremented to %"PRIu8, + pstate->version); + + if (flags & STREAM_EOF) + AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_EOF); + + alstate = f->alstate; + if (alstate == NULL) { + f->alstate = alstate = p->StateAlloc(); + if (alstate == NULL) + goto error; + SCLogDebug("alloced new app layer state %p (name %s)", + alstate, AppLayerGetProtoName(f->alproto)); + } else { + SCLogDebug("using existing app layer state %p (name %s))", + alstate, AppLayerGetProtoName(f->alproto)); + } + + /* invoke the recursive parser, but only on data. We may get empty msgs on EOF */ + if (input_len > 0 || (flags & STREAM_EOF)) { + /* invoke the parser */ + if (p->Parser[(flags & STREAM_TOSERVER) ? 0 : 1](f, alstate, pstate, + input, input_len, + alp_tctx->alproto_local_storage[f->protomap][alproto]) < 0) + { + goto error; + } + } + + /* set the packets to no inspection and reassembly if required */ + if (pstate->flags & APP_LAYER_PARSER_NO_INSPECTION) { + AppLayerParserSetEOF(pstate); + FlowSetNoPayloadInspectionFlag(f); + + if (f->proto == IPPROTO_TCP) { + StreamTcpDisableAppLayer(f); + + /* Set the no reassembly flag for both the stream in this TcpSession */ + if (pstate->flags & APP_LAYER_PARSER_NO_REASSEMBLY) { + /* Used only if it's TCP */ + TcpSession *ssn = f->protoctx; + if (ssn != NULL) { + StreamTcpSetSessionNoReassemblyFlag(ssn, + flags & STREAM_TOCLIENT ? 1 : 0); + StreamTcpSetSessionNoReassemblyFlag(ssn, + flags & STREAM_TOSERVER ? 1 : 0); + } + } + } + } + + /* In cases like HeartBleed for TLS we need to inspect AppLayer but not Payload */ + if (!(f->flags & FLOW_NOPAYLOAD_INSPECTION) && pstate->flags & APP_LAYER_PARSER_NO_INSPECTION_PAYLOAD) { + FlowSetNoPayloadInspectionFlag(f); + /* Set the no reassembly flag for both the stream in this TcpSession */ + if (f->proto == IPPROTO_TCP) { + /* Used only if it's TCP */ + TcpSession *ssn = f->protoctx; + if (ssn != NULL) { + StreamTcpSetDisableRawReassemblyFlag(ssn, 0); + StreamTcpSetDisableRawReassemblyFlag(ssn, 1); + } + } + } + + /* next, see if we can get rid of transactions now */ + AppLayerParserTransactionsCleanup(f); + + /* stream truncated, inform app layer */ + if (flags & STREAM_DEPTH) + AppLayerParserStreamTruncated(f->proto, alproto, alstate, flags); + + end: + SCReturnInt(0); + error: + /* Set the no app layer inspection flag for both + * the stream in this Flow */ + if (f->proto == IPPROTO_TCP) { + StreamTcpDisableAppLayer(f); + } + AppLayerParserSetEOF(pstate); + SCReturnInt(-1); +} + +void AppLayerParserSetEOF(AppLayerParserState *pstate) +{ + SCEnter(); + + if (pstate == NULL) + goto end; + + AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_EOF); + /* increase version so we will inspect it one more time + * with the EOF flags now set */ + pstate->version++; + + end: + SCReturn; +} + +int AppLayerParserHasDecoderEvents(uint8_t ipproto, AppProto alproto, + void *alstate, AppLayerParserState *pstate, + uint8_t flags) +{ + SCEnter(); + + if (alstate == NULL || pstate == NULL) + goto not_present; + + AppLayerDecoderEvents *decoder_events; + uint64_t tx_id; + uint64_t max_id; + + if (AppLayerParserProtocolIsTxEventAware(ipproto, alproto)) { + /* fast path if supported by alproto */ + if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].StateHasEvents != NULL) { + if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + StateHasEvents(alstate) == 1) + { + goto present; + } + } else { + /* check each tx */ + tx_id = AppLayerParserGetTransactionInspectId(pstate, flags); + max_id = AppLayerParserGetTxCnt(ipproto, alproto, alstate); + for ( ; tx_id < max_id; tx_id++) { + decoder_events = AppLayerParserGetEventsByTx(ipproto, alproto, alstate, tx_id); + if (decoder_events && decoder_events->cnt) + goto present; + } + } + } + + decoder_events = AppLayerParserGetDecoderEvents(pstate); + if (decoder_events && decoder_events->cnt) + goto present; + + /* if we have reached here, we don't have events */ + not_present: + SCReturnInt(0); + present: + SCReturnInt(1); +} + +int AppLayerParserProtocolIsTxAware(uint8_t ipproto, AppProto alproto) +{ + SCEnter(); + int ipproto_map = FlowGetProtoMapping(ipproto); + int r = (alp_ctx.ctxs[ipproto_map][alproto].StateGetTx == NULL) ? 0 : 1; + SCReturnInt(r); +} + +int AppLayerParserProtocolIsTxEventAware(uint8_t ipproto, AppProto alproto) +{ + SCEnter(); + int ipproto_map = FlowGetProtoMapping(ipproto); + int r = (alp_ctx.ctxs[ipproto_map][alproto].StateGetEvents == NULL) ? 0 : 1; + SCReturnInt(r); +} + +int AppLayerParserProtocolSupportsTxs(uint8_t ipproto, AppProto alproto) +{ + SCEnter(); + int ipproto_map = FlowGetProtoMapping(ipproto); + int r = (alp_ctx.ctxs[ipproto_map][alproto].StateTransactionFree == NULL) ? 0 : 1; + SCReturnInt(r); +} + +int AppLayerParserProtocolHasLogger(uint8_t ipproto, AppProto alproto) +{ + SCEnter(); + int ipproto_map = FlowGetProtoMapping(ipproto); + int r = (alp_ctx.ctxs[ipproto_map][alproto].logger == 0) ? 0 : 1; + SCReturnInt(r); +} + +void AppLayerParserTriggerRawStreamReassembly(Flow *f) +{ + SCEnter(); + + if (f != NULL && f->protoctx != NULL) + StreamTcpReassembleTriggerRawReassembly(f->protoctx); + + SCReturn; +} + +/***** Cleanup *****/ + +void AppLayerParserStateCleanup(uint8_t ipproto, AppProto alproto, void *alstate, + AppLayerParserState *pstate) +{ + SCEnter(); + + AppLayerParserProtoCtx *ctx = &alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]; + + if (ctx->StateFree != NULL && alstate != NULL) + ctx->StateFree(alstate); + + /* free the app layer parser api state */ + if (pstate != NULL) + AppLayerParserStateFree(pstate); + + SCReturn; +} + + +void AppLayerParserRegisterProtocolParsers(void) +{ + SCEnter(); + + RegisterHTPParsers(); + RegisterSSLParsers(); + RegisterSMBParsers(); + /** \todo bug 719 */ + //RegisterSMB2Parsers(); + RegisterDCERPCParsers(); + RegisterDCERPCUDPParsers(); + RegisterFTPParsers(); + RegisterSSHParsers(); + RegisterSMTPParsers(); + RegisterDNSUDPParsers(); + RegisterDNSTCPParsers(); + RegisterModbusParsers(); + + /** IMAP */ + AppLayerProtoDetectRegisterProtocol(ALPROTO_IMAP, "imap"); + if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", "imap")) { + if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_IMAP, + "1|20|capability", 12, 0, STREAM_TOSERVER) < 0) + { + SCLogInfo("imap proto registration failure\n"); + exit(EXIT_FAILURE); + } + } else { + SCLogInfo("Protocol detection and parser disabled for %s protocol.", + "imap"); + } + + /** MSN Messenger */ + AppLayerProtoDetectRegisterProtocol(ALPROTO_MSN, "msn"); + if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", "msn")) { + if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_MSN, + "msn", 10, 6, STREAM_TOSERVER) < 0) + { + SCLogInfo("msn proto registration failure\n"); + exit(EXIT_FAILURE); + } + } else { + SCLogInfo("Protocol detection and parser disabled for %s protocol.", + "msn"); + } + + return; +} + + +void AppLayerParserStateSetFlag(AppLayerParserState *pstate, uint8_t flag) +{ + SCEnter(); + pstate->flags |= flag; + SCReturn; +} + +int AppLayerParserStateIssetFlag(AppLayerParserState *pstate, uint8_t flag) +{ + SCEnter(); + SCReturnInt(pstate->flags & flag); +} + + +void AppLayerParserStreamTruncated(uint8_t ipproto, AppProto alproto, void *alstate, + uint8_t direction) +{ + SCEnter(); + + + if (alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].Truncate != NULL) + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto].Truncate(alstate, direction); + + SCReturn; +} + +#ifdef DEBUG +void AppLayerParserStatePrintDetails(AppLayerParserState *pstate) +{ + SCEnter(); + + if (pstate == NULL) + SCReturn; + + AppLayerParserState *p = pstate; + SCLogDebug("AppLayerParser parser state information for parser state p(%p). " + "p->inspect_id[0](%"PRIu64"), " + "p->inspect_id[1](%"PRIu64"), " + "p->log_id(%"PRIu64"), " + "p->version(%"PRIu8"), " + "p->decoder_events(%p).", + pstate, p->inspect_id[0], p->inspect_id[1], p->log_id, + p->version, p->decoder_events); + + SCReturn; +} +#endif + + +/***** Unittests *****/ + +#ifdef UNITTESTS + +static AppLayerParserCtx alp_ctx_backup_unittest; + +typedef struct TestState_ { + uint8_t test; +} TestState; + +/** + * \brief Test parser function to test the memory deallocation of app layer + * parser of occurence of an error. + */ +static int TestProtocolParser(Flow *f, void *test_state, AppLayerParserState *pstate, + uint8_t *input, uint32_t input_len, + void *local_data) +{ + SCEnter(); + SCReturnInt(-1); +} + +/** \brief Function to allocates the Test protocol state memory + */ +static void *TestProtocolStateAlloc(void) +{ + SCEnter(); + void *s = SCMalloc(sizeof(TestState)); + if (unlikely(s == NULL)) + goto end; + memset(s, 0, sizeof(TestState)); + end: + SCReturnPtr(s, "TestState"); +} + +/** \brief Function to free the Test Protocol state memory + */ +static void TestProtocolStateFree(void *s) +{ + SCFree(s); +} + +void AppLayerParserRegisterProtocolUnittests(uint8_t ipproto, AppProto alproto, + void (*RegisterUnittests)(void)) +{ + SCEnter(); + alp_ctx.ctxs[FlowGetProtoMapping(ipproto)][alproto]. + RegisterUnittests = RegisterUnittests; + SCReturn; +} + +void AppLayerParserBackupParserTable(void) +{ + SCEnter(); + alp_ctx_backup_unittest = alp_ctx; + memset(&alp_ctx, 0, sizeof(alp_ctx)); + SCReturn; +} + +void AppLayerParserRestoreParserTable(void) +{ + SCEnter(); + alp_ctx = alp_ctx_backup_unittest; + memset(&alp_ctx_backup_unittest, 0, sizeof(alp_ctx_backup_unittest)); + SCReturn; +} + +/** + * \test Test the deallocation of app layer parser memory on occurance of + * error in the parsing process. + */ +static int AppLayerParserTest01(void) +{ + AppLayerParserBackupParserTable(); + + int result = 0; + Flow *f = NULL; + uint8_t testbuf[] = { 0x11 }; + uint32_t testlen = sizeof(testbuf); + TcpSession ssn; + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + + memset(&ssn, 0, sizeof(ssn)); + + /* Register the Test protocol state and parser functions */ + AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TEST, STREAM_TOSERVER, + TestProtocolParser); + AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_TEST, + TestProtocolStateAlloc, TestProtocolStateFree); + + f = UTHBuildFlow(AF_INET, "1.2.3.4", "4.3.2.1", 20, 40); + if (f == NULL) + goto end; + f->protoctx = &ssn; + f->alproto = ALPROTO_TEST; + f->proto = IPPROTO_TCP; + + StreamTcpInitConfig(TRUE); + + SCMutexLock(&f->m); + int r = AppLayerParserParse(alp_tctx, f, ALPROTO_TEST, STREAM_TOSERVER|STREAM_EOF, + testbuf, testlen); + if (r != -1) { + printf("returned %" PRId32 ", expected -1: ", r); + SCMutexUnlock(&f->m); + goto end; + } + SCMutexUnlock(&f->m); + + if (!(ssn.flags & STREAMTCP_FLAG_APP_LAYER_DISABLED)) { + printf("flag should have been set, but is not: "); + goto end; + } + + result = 1; + end: + AppLayerParserRestoreParserTable(); + StreamTcpFreeConfig(TRUE); + + UTHFreeFlow(f); + return result; +} + +/** + * \test Test the deallocation of app layer parser memory on occurance of + * error in the parsing process for UDP. + */ +static int AppLayerParserTest02(void) +{ + AppLayerParserBackupParserTable(); + + int result = 1; + Flow *f = NULL; + uint8_t testbuf[] = { 0x11 }; + uint32_t testlen = sizeof(testbuf); + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + + /* Register the Test protocol state and parser functions */ + AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_TEST, STREAM_TOSERVER, + TestProtocolParser); + AppLayerParserRegisterStateFuncs(IPPROTO_UDP, ALPROTO_TEST, + TestProtocolStateAlloc, TestProtocolStateFree); + + f = UTHBuildFlow(AF_INET, "1.2.3.4", "4.3.2.1", 20, 40); + if (f == NULL) + goto end; + f->alproto = ALPROTO_TEST; + f->proto = IPPROTO_UDP; + f->protomap = FlowGetProtoMapping(f->proto); + + StreamTcpInitConfig(TRUE); + + SCMutexLock(&f->m); + int r = AppLayerParserParse(alp_tctx, f, ALPROTO_TEST, STREAM_TOSERVER|STREAM_EOF, testbuf, + testlen); + if (r != -1) { + printf("returned %" PRId32 ", expected -1: \n", r); + result = 0; + SCMutexUnlock(&f->m); + goto end; + } + SCMutexUnlock(&f->m); + + end: + AppLayerParserRestoreParserTable(); + StreamTcpFreeConfig(TRUE); + UTHFreeFlow(f); + return result; +} + + +void AppLayerParserRegisterUnittests(void) +{ + SCEnter(); + + int ip; + AppProto alproto; + AppLayerParserProtoCtx *ctx; + + for (ip = 0; ip < FLOW_PROTO_DEFAULT; ip++) { + for (alproto = 0; alproto < ALPROTO_MAX; alproto++) { + ctx = &alp_ctx.ctxs[ip][alproto]; + if (ctx->RegisterUnittests == NULL) + continue; + ctx->RegisterUnittests(); + } + } + + UtRegisterTest("AppLayerParserTest01", AppLayerParserTest01, 1); + UtRegisterTest("AppLayerParserTest02", AppLayerParserTest02, 1); + + SCReturn; +} + +#endif |