diff options
author | Ashlee Young <ashlee@onosfw.com> | 2015-09-22 12:53:15 -0700 |
---|---|---|
committer | Ashlee Young <ashlee@onosfw.com> | 2015-09-22 12:53:15 -0700 |
commit | e6d71622143ff9b2421a1abbe8434b954b5b1099 (patch) | |
tree | c5b037b78e9170e62538c72ab604189c47159a80 /framework/src/suricata/src/app-layer-template.c | |
parent | 81391595dca425ae58e2294898f09f11d9a32dbc (diff) |
bringing suricata to commit 4a738023d5ac945f0109ceb13fcc43e3f3095453
Change-Id: I7bbd8767089a43573cb38d23fe7bf1b656b29893
Diffstat (limited to 'framework/src/suricata/src/app-layer-template.c')
-rw-r--r-- | framework/src/suricata/src/app-layer-template.c | 533 |
1 files changed, 533 insertions, 0 deletions
diff --git a/framework/src/suricata/src/app-layer-template.c b/framework/src/suricata/src/app-layer-template.c new file mode 100644 index 00000000..87b64b73 --- /dev/null +++ b/framework/src/suricata/src/app-layer-template.c @@ -0,0 +1,533 @@ +/* Copyright (C) 2015 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 Template application layer detector and parser for learning and + * template pruposes. + * + * This template implements a simple application layer for something + * like the echo protocol running on port 7. + */ + +#include "suricata-common.h" +#include "stream.h" + +#include "util-unittest.h" + +#include "app-layer-detect-proto.h" +#include "app-layer-parser.h" + +#include "app-layer-template.h" + +/* The default port to probe for echo traffic if not provided in the + * configuration file. */ +#define TEMPLATE_DEFAULT_PORT "7" + +/* The minimum size for an echo message. For some protocols this might + * be the size of a header. */ +#define TEMPLATE_MIN_FRAME_LEN 1 + +/* Enum of app-layer events for an echo protocol. Normally you might + * have events for errors in parsing data, like unexpected data being + * received. For echo we'll make something up, and log an app-layer + * level alert if an empty message is received. + * + * Example rule: + * + * alert template any any -> any any (msg:"SURCATA Template empty message"; \ + * app-layer-event:template.empty_message; sid:X; rev:Y;) + */ +enum { + TEMPLATE_DECODER_EVENT_EMPTY_MESSAGE, +}; + +SCEnumCharMap template_decoder_event_table[] = { + {"EMPTY_MESSAGE", TEMPLATE_DECODER_EVENT_EMPTY_MESSAGE}, +}; + +static TemplateTransaction *TemplateTxAlloc(TemplateState *echo) +{ + TemplateTransaction *tx = SCCalloc(1, sizeof(TemplateTransaction)); + if (unlikely(tx == NULL)) { + return NULL; + } + + /* Increment the transaction ID on the state each time one is + * allocated. */ + tx->tx_id = echo->transaction_max++; + + TAILQ_INSERT_TAIL(&echo->tx_list, tx, next); + + return tx; +} + +static void TemplateTxFree(void *tx) +{ + TemplateTransaction *templatetx = tx; + + if (templatetx->request_buffer != NULL) { + SCFree(templatetx->request_buffer); + } + + if (templatetx->response_buffer != NULL) { + SCFree(templatetx->response_buffer); + } + + AppLayerDecoderEventsFreeEvents(&templatetx->decoder_events); + + SCFree(tx); +} + +static void *TemplateStateAlloc(void) +{ + SCLogNotice("Allocating template state."); + TemplateState *state = SCCalloc(1, sizeof(TemplateState)); + if (unlikely(state == NULL)) { + return NULL; + } + TAILQ_INIT(&state->tx_list); + return state; +} + +static void TemplateStateFree(void *state) +{ + TemplateState *template_state = state; + TemplateTransaction *tx; + SCLogNotice("Freeing template state."); + while ((tx = TAILQ_FIRST(&template_state->tx_list)) != NULL) { + TAILQ_REMOVE(&template_state->tx_list, tx, next); + TemplateTxFree(tx); + } + SCFree(template_state); +} + +/** + * \brief Callback from the application layer to have a transaction freed. + * + * \param state a void pointer to the TemplateState object. + * \param tx_id the transaction ID to free. + */ +static void TemplateStateTxFree(void *state, uint64_t tx_id) +{ + TemplateState *echo = state; + TemplateTransaction *tx = NULL, *ttx; + + SCLogNotice("Freeing transaction %"PRIu64, tx_id); + + TAILQ_FOREACH_SAFE(tx, &echo->tx_list, next, ttx) { + + /* Continue if this is not the transaction we are looking + * for. */ + if (tx->tx_id != tx_id) { + continue; + } + + /* Remove and free the transaction. */ + TAILQ_REMOVE(&echo->tx_list, tx, next); + TemplateTxFree(tx); + return; + } + + SCLogNotice("Transaction %"PRIu64" not found.", tx_id); +} + +static int TemplateStateGetEventInfo(const char *event_name, int *event_id, + AppLayerEventType *event_type) +{ + *event_id = SCMapEnumNameToValue(event_name, template_decoder_event_table); + if (*event_id == -1) { + SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in " + "template enum map table.", event_name); + /* This should be treated as fatal. */ + return -1; + } + + *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION; + + return 0; +} + +static AppLayerDecoderEvents *TemplateGetEvents(void *state, uint64_t tx_id) +{ + TemplateState *template_state = state; + TemplateTransaction *tx; + + TAILQ_FOREACH(tx, &template_state->tx_list, next) { + if (tx->tx_id == tx_id) { + return tx->decoder_events; + } + } + + return NULL; +} + +static int TemplateHasEvents(void *state) +{ + TemplateState *echo = state; + return echo->events; +} + +/** + * \brief Probe the input to see if it looks like echo. + * + * \retval ALPROTO_TEMPLATE if it looks like echo, otherwise + * ALPROTO_UNKNOWN. + */ +static AppProto TemplateProbingParser(uint8_t *input, uint32_t input_len, + uint32_t *offset) +{ + /* Very simple test - if there is input, this is echo. */ + if (input_len >= TEMPLATE_MIN_FRAME_LEN) { + SCLogNotice("Detected as ALPROTO_TEMPLATE."); + return ALPROTO_TEMPLATE; + } + + SCLogNotice("Protocol not detected as ALPROTO_TEMPLATE."); + return ALPROTO_UNKNOWN; +} + +static int TemplateParseRequest(Flow *f, void *state, + AppLayerParserState *pstate, uint8_t *input, uint32_t input_len, + void *local_data) +{ + TemplateState *echo = state; + + SCLogNotice("Parsing echo request: len=%"PRIu32, input_len); + + /* Likely connection closed, we can just return here. */ + if ((input == NULL || input_len == 0) && + AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) { + return 0; + } + + /* Probably don't want to create a transaction in this case + * either. */ + if (input == NULL || input_len == 0) { + return 0; + } + + /* Normally you would parse out data here and store it in the + * transaction object, but as this is echo, we'll just record the + * request data. */ + + /* Also, if this protocol may have a "protocol data unit" span + * multiple chunks of data, which is always a possibility with + * TCP, you may need to do some buffering here. + * + * For the sake of simplicity, buffering is left out here, but + * even for an echo protocol we may want to buffer until a new + * line is seen, assuming its text based. + */ + + /* Allocate a transaction. + * + * But note that if a "protocol data unit" is not received in one + * chunk of data, and the buffering is done on the transaction, we + * may need to look for the transaction that this newly recieved + * data belongs to. + */ + TemplateTransaction *tx = TemplateTxAlloc(echo); + if (unlikely(tx == NULL)) { + SCLogNotice("Failed to allocate new Template tx."); + goto end; + } + SCLogNotice("Allocated Template tx %"PRIu64".", tx->tx_id); + + /* Make a copy of the request. */ + tx->request_buffer = SCCalloc(1, input_len); + if (unlikely(tx->request_buffer == NULL)) { + goto end; + } + memcpy(tx->request_buffer, input, input_len); + tx->request_buffer_len = input_len; + + /* Here we check for an empty message and create an app-layer + * event. */ + if ((input_len == 1 && tx->request_buffer[0] == '\n') || + (input_len == 2 && tx->request_buffer[0] == '\r')) { + SCLogNotice("Creating event for empty message."); + AppLayerDecoderEventsSetEventRaw(&tx->decoder_events, + TEMPLATE_DECODER_EVENT_EMPTY_MESSAGE); + echo->events++; + } + +end: + return 0; +} + +static int TemplateParseResponse(Flow *f, void *state, AppLayerParserState *pstate, + uint8_t *input, uint32_t input_len, void *local_data) +{ + TemplateState *echo = state; + TemplateTransaction *tx = NULL, *ttx;; + + SCLogNotice("Parsing Template response."); + + /* Likely connection closed, we can just return here. */ + if ((input == NULL || input_len == 0) && + AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) { + return 0; + } + + /* Probably don't want to create a transaction in this case + * either. */ + if (input == NULL || input_len == 0) { + return 0; + } + + /* Look up the existing transaction for this response. In the case + * of echo, it will be the most recent transaction on the + * TemplateState object. */ + + /* We should just grab the last transaction, but this is to + * illustrate how you might traverse the transaction list to find + * the transaction associated with this response. */ + TAILQ_FOREACH(ttx, &echo->tx_list, next) { + tx = ttx; + } + + if (tx == NULL) { + SCLogNotice("Failed to find transaction for response on echo state %p.", + echo); + goto end; + } + + SCLogNotice("Found transaction %"PRIu64" for response on echo state %p.", + tx->tx_id, echo); + + /* If the protocol requires multiple chunks of data to complete, you may + * run into the case where you have existing response data. + * + * In this case, we just log that there is existing data and free it. But + * you might want to realloc the buffer and append the data. + */ + if (tx->response_buffer != NULL) { + SCLogNotice("WARNING: Transaction already has response data, " + "existing data will be overwritten."); + SCFree(tx->response_buffer); + } + + /* Make a copy of the response. */ + tx->response_buffer = SCCalloc(1, input_len); + if (unlikely(tx->response_buffer == NULL)) { + goto end; + } + memcpy(tx->response_buffer, input, input_len); + tx->response_buffer_len = input_len; + + /* Set the response_done flag for transaction state checking in + * TemplateGetStateProgress(). */ + tx->response_done = 1; + +end: + return 0; +} + +static uint64_t TemplateGetTxCnt(void *state) +{ + TemplateState *echo = state; + SCLogNotice("Current tx count is %"PRIu64".", echo->transaction_max); + return echo->transaction_max; +} + +static void *TemplateGetTx(void *state, uint64_t tx_id) +{ + TemplateState *echo = state; + TemplateTransaction *tx; + + SCLogNotice("Requested tx ID %"PRIu64".", tx_id); + + TAILQ_FOREACH(tx, &echo->tx_list, next) { + if (tx->tx_id == tx_id) { + SCLogNotice("Transaction %"PRIu64" found, returning tx object %p.", + tx_id, tx); + return tx; + } + } + + SCLogNotice("Transaction ID %"PRIu64" not found.", tx_id); + return NULL; +} + +/** + * \brief Called by the application layer. + * + * In most cases 1 can be returned here. + */ +static int TemplateGetAlstateProgressCompletionStatus(uint8_t direction) { + return 1; +} + +/** + * \brief Return the state of a transaction in a given direction. + * + * In the case of the echo protocol, the existence of a transaction + * means that the request is done. However, some protocols that may + * need multiple chunks of data to complete the request may need more + * than just the existence of a transaction for the request to be + * considered complete. + * + * For the response to be considered done, the response for a request + * needs to be seen. The response_done flag is set on response for + * checking here. + */ +static int TemplateGetStateProgress(void *tx, uint8_t direction) +{ + TemplateTransaction *echotx = tx; + + SCLogNotice("Transaction progress requested for tx ID %"PRIu64 + ", direction=0x%02x", echotx->tx_id, direction); + + if (direction & STREAM_TOCLIENT && echotx->response_done) { + return 1; + } + else if (direction & STREAM_TOSERVER) { + /* For echo, just the existence of the transaction means the + * request is done. */ + return 1; + } + + return 0; +} + +/** + * \brief ??? + */ +static DetectEngineState *TemplateGetTxDetectState(void *vtx) +{ + TemplateTransaction *tx = vtx; + return tx->de_state; +} + +/** + * \brief ??? + */ +static int TemplateSetTxDetectState(void *state, void *vtx, + DetectEngineState *s) +{ + TemplateTransaction *tx = vtx; + tx->de_state = s; + return 0; +} + +void RegisterTemplateParsers(void) +{ + char *proto_name = "template"; + + /* Check if Template TCP detection is enabled. If it does not exist in + * the configuration file then it will be enabled by default. */ + if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) { + + SCLogNotice("Template TCP protocol detection enabled."); + + AppLayerProtoDetectRegisterProtocol(ALPROTO_TEMPLATE, proto_name); + + if (RunmodeIsUnittests()) { + + SCLogNotice("Unittest mode, registeringd default configuration."); + AppLayerProtoDetectPPRegister(IPPROTO_TCP, TEMPLATE_DEFAULT_PORT, + ALPROTO_TEMPLATE, 0, TEMPLATE_MIN_FRAME_LEN, STREAM_TOSERVER, + TemplateProbingParser); + + } + else { + + if (!AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP, + proto_name, ALPROTO_TEMPLATE, 0, TEMPLATE_MIN_FRAME_LEN, + TemplateProbingParser)) { + SCLogNotice("No echo app-layer configuration, enabling echo" + " detection TCP detection on port %s.", + TEMPLATE_DEFAULT_PORT); + AppLayerProtoDetectPPRegister(IPPROTO_TCP, + TEMPLATE_DEFAULT_PORT, ALPROTO_TEMPLATE, 0, + TEMPLATE_MIN_FRAME_LEN, STREAM_TOSERVER, + TemplateProbingParser); + } + + } + + } + + else { + SCLogNotice("Protocol detecter and parser disabled for Template."); + return; + } + + if (AppLayerParserConfParserEnabled("udp", proto_name)) { + + SCLogNotice("Registering Template protocol parser."); + + /* Register functions for state allocation and freeing. A + * state is allocated for every new Template flow. */ + AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_TEMPLATE, + TemplateStateAlloc, TemplateStateFree); + + /* Register request parser for parsing frame from server to client. */ + AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TEMPLATE, + STREAM_TOSERVER, TemplateParseRequest); + + /* Register response parser for parsing frames from server to client. */ + AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TEMPLATE, STREAM_TOCLIENT, + TemplateParseResponse); + + /* Register a function to be called by the application layer + * when a transaction is to be freed. */ + AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_TEMPLATE, + TemplateStateTxFree); + + /* Register a function to return the current transaction count. */ + AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_TEMPLATE, TemplateGetTxCnt); + + /* Transaction handling. */ + AppLayerParserRegisterGetStateProgressCompletionStatus(IPPROTO_TCP, + ALPROTO_TEMPLATE, TemplateGetAlstateProgressCompletionStatus); + AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, + ALPROTO_TEMPLATE, TemplateGetStateProgress); + AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_TEMPLATE, + TemplateGetTx); + + /* Application layer event handling. */ + AppLayerParserRegisterHasEventsFunc(IPPROTO_TCP, ALPROTO_TEMPLATE, + TemplateHasEvents); + + /* What is this being registered for? */ + AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_TEMPLATE, + NULL, TemplateGetTxDetectState, TemplateSetTxDetectState); + + AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_TEMPLATE, + TemplateStateGetEventInfo); + AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_TEMPLATE, + TemplateGetEvents); + } + else { + SCLogNotice("Template protocol parsing disabled."); + } + +#ifdef UNITTESTS + AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_TEMPLATE, + TemplateParserRegisterTests); +#endif +} + +#ifdef UNITTESTS +#endif + +void TemplateParserRegisterTests(void) +{ +#ifdef UNITTESTS +#endif +} |