aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/suricata/src/app-layer-template.c
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/suricata/src/app-layer-template.c')
-rw-r--r--framework/src/suricata/src/app-layer-template.c533
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
+}