]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect: http_start sticky buffer
authorVictor Julien <victor@inliniac.net>
Tue, 20 Dec 2016 13:04:55 +0000 (14:04 +0100)
committerVictor Julien <victor@inliniac.net>
Thu, 16 Feb 2017 09:35:41 +0000 (10:35 +0100)
Matches on the start of a HTTP request or response.

Uses a buffer constructed from the request line and normalized request
headers, including the Cookie header.

Or for the response side, it uses the response line plus the
normalized response headers, including the Set-Cookie header.

Both buffers are terminated by an extra \r\n.

src/Makefile.am
src/detect-http-start.c [new file with mode: 0644]
src/detect-http-start.h [new file with mode: 0644]
src/detect.c
src/detect.h

index 29d1d606984c0bfe23e5b1048fed55d287a163b7..7d505307dba28d10692308d702b463d3dcabc7c1 100644 (file)
@@ -175,6 +175,7 @@ detect-http-raw-uri.c detect-http-raw-uri.h \
 detect-http-request-line.c detect-http-request-line.h \
 detect-http-response-line.c detect-http-response-line.h \
 detect-http-server-body.c detect-http-server-body.h \
+detect-http-start.c detect-http-start.h \
 detect-http-stat-code.c detect-http-stat-code.h \
 detect-http-stat-msg.c detect-http-stat-msg.h \
 detect-http-ua.c detect-http-ua.h \
diff --git a/src/detect-http-start.c b/src/detect-http-start.c
new file mode 100644 (file)
index 0000000..b414930
--- /dev/null
@@ -0,0 +1,337 @@
+/* Copyright (C) 2007-2017 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.
+ */
+
+/**
+ * \ingroup httplayer
+ *
+ * @{
+ */
+
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements http_start
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "decode.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+#include "detect-engine-prefilter.h"
+#include "detect-engine-content-inspection.h"
+#include "detect-content.h"
+#include "detect-pcre.h"
+#include "detect-http-header-common.h"
+
+#include "flow.h"
+#include "flow-var.h"
+#include "flow-util.h"
+
+#include "util-debug.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-spm.h"
+#include "util-print.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "app-layer-htp.h"
+#include "detect-http-header.h"
+#include "stream-tcp.h"
+
+#include "util-print.h"
+
+#define KEYWORD_NAME "http_start"
+#define KEYWORD_DOC "http-keywords#http-start"
+#define BUFFER_NAME "http_start"
+#define BUFFER_DESC "http start: request/response line + headers"
+static int g_buffer_id = 0;
+static int g_keyword_thread_id = 0;
+
+#define BUFFER_TX_STEP      4
+#define BUFFER_SIZE_STEP    2048
+static HttpHeaderThreadDataConfig g_td_config = { BUFFER_TX_STEP, BUFFER_SIZE_STEP };
+
+static uint8_t *GetBufferForTX(htp_tx_t *tx, uint64_t tx_id,
+        DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+        Flow *f, HtpState *htp_state, uint8_t flags,
+        uint32_t *buffer_len)
+{
+    *buffer_len = 0;
+
+    HttpHeaderThreadData *hdr_td = NULL;
+    HttpHeaderBuffer *buf = HttpHeaderGetBufferSpaceForTXID(det_ctx, f, flags,
+            tx_id, g_keyword_thread_id, &hdr_td);
+    if (unlikely(buf == NULL)) {
+        return NULL;
+    } else if (buf->len > 0) {
+        /* already filled buf, reuse */
+        *buffer_len = buf->len;
+        return buf->buffer;
+    }
+
+    bstr *line = NULL;
+    htp_table_t *headers;
+    if (flags & STREAM_TOSERVER) {
+        if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) <=
+                HTP_REQUEST_HEADERS)
+            return NULL;
+        line = tx->request_line;
+        headers = tx->request_headers;
+    } else {
+        if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) <=
+                HTP_RESPONSE_HEADERS)
+            return NULL;
+        headers = tx->response_headers;
+        line = tx->response_line;
+    }
+    if (headers == NULL)
+        return NULL;
+
+    size_t line_size = bstr_len(line) + 2;
+    if (line_size + buf->len > buf->size) {
+        if (HttpHeaderExpandBuffer(hdr_td, buf, line_size) != 0) {
+            return NULL;
+        }
+    }
+    memcpy(buf->buffer + buf->len, bstr_ptr(line), bstr_size(line));
+    buf->len += bstr_size(line);
+    buf->buffer[buf->len++] = '\r';
+    buf->buffer[buf->len++] = '\n';
+
+    size_t i = 0;
+    size_t no_of_headers = htp_table_size(headers);
+    for (; i < no_of_headers; i++) {
+        htp_header_t *h = htp_table_get_index(headers, i, NULL);
+        size_t size1 = bstr_size(h->name);
+        size_t size2 = bstr_size(h->value);
+#if 0
+        if (flags & STREAM_TOSERVER) {
+            if (size1 == 6 &&
+                SCMemcmpLowercase("cookie", bstr_ptr(h->name), 6) == 0) {
+                continue;
+            }
+        } else {
+            if (size1 == 10 &&
+                SCMemcmpLowercase("set-cookie", bstr_ptr(h->name), 10) == 0) {
+                continue;
+            }
+        }
+#endif
+        size_t size = size1 + size2 + 4;
+        if (i + 1 == no_of_headers)
+            size += 2;
+        if (size + buf->len > buf->size) {
+            if (HttpHeaderExpandBuffer(hdr_td, buf, size) != 0) {
+                return NULL;
+            }
+        }
+
+        memcpy(buf->buffer + buf->len, bstr_ptr(h->name), bstr_size(h->name));
+        buf->len += bstr_size(h->name);
+        buf->buffer[buf->len++] = ':';
+        buf->buffer[buf->len++] = ' ';
+        memcpy(buf->buffer + buf->len, bstr_ptr(h->value), bstr_size(h->value));
+        buf->len += bstr_size(h->value);
+        buf->buffer[buf->len++] = '\r';
+        buf->buffer[buf->len++] = '\n';
+        if (i + 1 == no_of_headers) {
+            buf->buffer[buf->len++] = '\r';
+            buf->buffer[buf->len++] = '\n';
+        }
+    }
+
+    *buffer_len = buf->len;
+    return buf->buffer;
+}
+
+/** \brief HTTP Start Mpm prefilter callback
+ *
+ *  \param det_ctx detection engine thread ctx
+ *  \param p packet to inspect
+ *  \param f flow to inspect
+ *  \param txv tx to inspect
+ *  \param pectx inspection context
+ */
+static void PrefilterTxHttpRequestStart(DetectEngineThreadCtx *det_ctx,
+        const void *pectx,
+        Packet *p, Flow *f, void *txv,
+        const uint64_t idx, const uint8_t flags)
+{
+    SCEnter();
+
+    const MpmCtx *mpm_ctx = (MpmCtx *)pectx;
+    htp_tx_t *tx = (htp_tx_t *)txv;
+
+    HtpState *htp_state = f->alstate;
+    uint32_t buffer_len = 0;
+    const uint8_t *buffer = GetBufferForTX(tx, idx,
+            NULL, det_ctx, f, htp_state,
+            flags, &buffer_len);
+
+    if (buffer_len >= mpm_ctx->minlen) {
+        (void)mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx,
+                &det_ctx->mtcu, &det_ctx->pmq, buffer, buffer_len);
+    }
+}
+
+static int PrefilterTxHttpRequestStartRegister(SigGroupHead *sgh, MpmCtx *mpm_ctx)
+{
+    SCEnter();
+
+    int r = PrefilterAppendTxEngine(sgh, PrefilterTxHttpRequestStart,
+        ALPROTO_HTTP, HTP_REQUEST_HEADERS,
+        mpm_ctx, NULL, KEYWORD_NAME " (request)");
+    return r;
+}
+
+/** \brief HTTP Start Mpm prefilter callback
+ *
+ *  \param det_ctx detection engine thread ctx
+ *  \param p packet to inspect
+ *  \param f flow to inspect
+ *  \param txv tx to inspect
+ *  \param pectx inspection context
+ */
+static void PrefilterTxHttpResponseStart(DetectEngineThreadCtx *det_ctx,
+        const void *pectx,
+        Packet *p, Flow *f, void *txv,
+        const uint64_t idx, const uint8_t flags)
+{
+    SCEnter();
+
+    const MpmCtx *mpm_ctx = (MpmCtx *)pectx;
+    htp_tx_t *tx = (htp_tx_t *)txv;
+
+    HtpState *htp_state = f->alstate;
+    uint32_t buffer_len = 0;
+    const uint8_t *buffer = GetBufferForTX(tx, idx, NULL, det_ctx,
+            f, htp_state, flags, &buffer_len);
+
+    if (buffer_len >= mpm_ctx->minlen) {
+        (void)mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx,
+                &det_ctx->mtcu, &det_ctx->pmq, buffer, buffer_len);
+    }
+}
+
+static int PrefilterTxHttpResponseStartRegister(SigGroupHead *sgh, MpmCtx *mpm_ctx)
+{
+    SCEnter();
+
+    int r = PrefilterAppendTxEngine(sgh, PrefilterTxHttpResponseStart,
+        ALPROTO_HTTP, HTP_RESPONSE_HEADERS,
+        mpm_ctx, NULL, KEYWORD_NAME " (response)");
+    return r;
+}
+
+static int InspectEngineHttpStart(ThreadVars *tv,
+        DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+        const Signature *s, const SigMatchData *smd,
+        Flow *f, uint8_t flags, void *alstate, void *tx, uint64_t tx_id)
+{
+    HtpState *htp_state = (HtpState *)alstate;
+    uint32_t buffer_len = 0;
+    uint8_t *buffer = GetBufferForTX(tx, tx_id,
+            de_ctx, det_ctx, f, htp_state,
+            flags, &buffer_len);
+    if (buffer_len == 0)
+        goto end;
+
+    det_ctx->buffer_offset = 0;
+    det_ctx->discontinue_matching = 0;
+    det_ctx->inspection_recursion_counter = 0;
+    int r = DetectEngineContentInspection(de_ctx, det_ctx, s, smd,
+                                          f,
+                                          buffer, buffer_len,
+                                          0,
+                                          DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE, NULL);
+    if (r == 1)
+        return DETECT_ENGINE_INSPECT_SIG_MATCH;
+
+ end:
+    if (flags & STREAM_TOSERVER) {
+        if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_REQUEST_HEADERS)
+            return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+    } else {
+        if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) > HTP_RESPONSE_HEADERS)
+            return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
+    }
+    return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+}
+
+static int DetectHttpStartSetup(DetectEngineCtx *de_ctx, Signature *s, char *arg)
+{
+    s->init_data->list = g_buffer_id;
+    return 0;
+}
+
+static void DetectHttpStartSetupCallback(Signature *s)
+{
+    SCLogDebug("callback invoked by %u", s->id);
+    s->mask |= SIG_MASK_REQUIRE_HTTP_STATE;
+}
+
+/**
+ * \brief Registers the keyword handlers for the "http_header" keyword.
+ */
+void DetectHttpStartRegister(void)
+{
+    sigmatch_table[DETECT_AL_HTTP_START].name = KEYWORD_NAME;
+    sigmatch_table[DETECT_AL_HTTP_START].desc = BUFFER_NAME " sticky buffer";
+    sigmatch_table[DETECT_AL_HTTP_START].url = DOC_URL DOC_VERSION "/rules/" KEYWORD_DOC;
+    sigmatch_table[DETECT_AL_HTTP_START].Setup = DetectHttpStartSetup;
+
+    sigmatch_table[DETECT_AL_HTTP_START].flags |= SIGMATCH_NOOPT ;
+    sigmatch_table[DETECT_AL_HTTP_START].flags |= SIGMATCH_PAYLOAD ;
+
+    DetectAppLayerMpmRegister(BUFFER_NAME, SIG_FLAG_TOSERVER, 2,
+            PrefilterTxHttpRequestStartRegister);
+    DetectAppLayerMpmRegister(BUFFER_NAME, SIG_FLAG_TOCLIENT, 2,
+            PrefilterTxHttpResponseStartRegister);
+
+    DetectAppLayerInspectEngineRegister(BUFFER_NAME,
+            ALPROTO_HTTP, SIG_FLAG_TOSERVER,
+            InspectEngineHttpStart);
+    DetectAppLayerInspectEngineRegister(BUFFER_NAME,
+            ALPROTO_HTTP, SIG_FLAG_TOCLIENT,
+            InspectEngineHttpStart);
+
+    DetectBufferTypeSetDescriptionByName(BUFFER_NAME,
+            BUFFER_DESC);
+
+    DetectBufferTypeRegisterSetupCallback(BUFFER_NAME,
+            DetectHttpStartSetupCallback);
+
+    g_buffer_id = DetectBufferTypeGetByName(BUFFER_NAME);
+
+    g_keyword_thread_id = DetectRegisterThreadCtxGlobalFuncs(KEYWORD_NAME,
+            HttpHeaderThreadDataInit, &g_td_config, HttpHeaderThreadDataFree);
+
+    SCLogDebug("keyword %s registered. Thread id %d. "
+            "Buffer %s registered. Buffer id %d",
+            KEYWORD_NAME, g_keyword_thread_id,
+            BUFFER_NAME, g_buffer_id);
+}
diff --git a/src/detect-http-start.h b/src/detect-http-start.h
new file mode 100644 (file)
index 0000000..52cada1
--- /dev/null
@@ -0,0 +1,29 @@
+/* Copyright (C) 2007-2016 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>
+ */
+
+#ifndef __DETECT_HTTP_START_H__
+#define __DETECT_HTTP_START_H__
+
+void DetectHttpStartRegister(void);
+
+#endif /* __DETECT_HTTP_START_H__ */
index 003d0342b9edd605a951bfec0bef962723c3bec9..8f34641d8aabcdabd09e62fd295ee2be817307de 100644 (file)
 #include "detect-http-raw-header.h"
 #include "detect-http-uri.h"
 #include "detect-http-protocol.h"
+#include "detect-http-start.h"
 #include "detect-http-raw-uri.h"
 #include "detect-http-stat-msg.h"
 #include "detect-http-request-line.h"
@@ -4057,6 +4058,7 @@ void SigTableSetup(void)
     DetectHttpHeaderRegister();
     DetectHttpHeaderNamesRegister();
     DetectHttpProtocolRegister();
+    DetectHttpStartRegister();
     DetectHttpRawHeaderRegister();
     DetectHttpMethodRegister();
     DetectHttpCookieRegister();
index 93edd58f397efca2ad5642c4b33d2a540b996fec..3800e75296d2fd4cf750384b0f33a0effa93c195 100644 (file)
@@ -1246,6 +1246,7 @@ enum {
     DETECT_AL_HTTP_COOKIE,
     DETECT_AL_HTTP_METHOD,
     DETECT_AL_HTTP_PROTOCOL,
+    DETECT_AL_HTTP_START,
     DETECT_AL_URILEN,
     DETECT_AL_HTTP_CLIENT_BODY,
     DETECT_AL_HTTP_SERVER_BODY,