]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
Adding XFF support to EVE alert output
authorDuarte Silva <development@serializing.me>
Fri, 5 Dec 2014 15:40:58 +0000 (15:40 +0000)
committerVictor Julien <victor@inliniac.net>
Tue, 16 Dec 2014 10:27:34 +0000 (11:27 +0100)
- Created app-layer-htp-xff.c and app-layer-htp-xff.h
- Added entries in the Makefile.am
- Added the necessary configuration options to EVE alert section
- Updated Unified2 XFF configuration comments and removed unnecessary whitespace
- Created a generic function to parse the configuration
- Release the flow locks sooner and remove debug logging
- Added XFF support to EVE alert output

src/Makefile.am
src/alert-unified2-alert.c
src/app-layer-htp-xff.c [new file with mode: 0644]
src/app-layer-htp-xff.h [new file with mode: 0644]
src/output-json-alert.c
suricata.yaml.in

index 6fac36e750bc202de2c7599d93e7328b8089bdc6..2ef8b3f0e65249320a970f55d07035960b5899e3 100644 (file)
@@ -26,6 +26,7 @@ app-layer-htp.c app-layer-htp.h \
 app-layer-htp-file.c app-layer-htp-file.h \
 app-layer-htp-libhtp.c app-layer-htp-libhtp.h \
 app-layer-htp-mem.c app-layer-htp-mem.h \
+app-layer-htp-xff.c app-layer-htp-xff.h \
 app-layer-modbus.c app-layer-modbus.h \
 app-layer-parser.c app-layer-parser.h \
 app-layer-protos.c app-layer-protos.h \
index 9a0a280682c0ab1a93b90e9a5930cdad1a78532b..330cf55bd98d49a83e04d5f31b12cb92e9e8c1e7 100644 (file)
@@ -58,6 +58,7 @@
 #include "app-layer-parser.h"
 #include "app-layer-htp.h"
 #include "app-layer.h"
+#include "app-layer-htp-xff.h"
 
 #include "output.h"
 #include "alert-unified2-alert.h"
@@ -179,29 +180,14 @@ typedef struct AlertUnified2Packet_ {
     uint8_t packet_data[4];         /**< packet data */
 } Unified2Packet;
 
-/** XFF is disabled */
-#define UNIFIED2_ALERT_XFF_DISABLED 1
-/** XFF extra data mode */
-#define UNIFIED2_ALERT_XFF_EXTRADATA 2
-/** XFF overwrite mode */
-#define UNIFIED2_ALERT_XFF_OVERWRITE 4
 /** Extracted XFF IP is v4 */
 #define UNIFIED2_ALERT_XFF_IPV4 8
 /** Extracted XFF IP is v4 */
 #define UNIFIED2_ALERT_XFF_IPV6 16
-/** Default XFF header name */
-#define UNIFIED2_ALERT_XFF_DEFAULT "X-Forwarded-For"
-/** Single XFF IP maximum length */
-#define UNIFIED2_ALERT_XFF_MAXLEN 46
-/** XFF header value minimal length */
-#define UNIFIED2_ALERT_XFF_CHAIN_MINLEN 7
-/** XFF header value maximum length */
-#define UNIFIED2_ALERT_XFF_CHAIN_MAXLEN 256
 
 typedef struct Unified2AlertFileCtx_ {
     LogFileCtx *file_ctx;
-    uint8_t xff_mode; /**< XFF operation mode */
-    char *xff_header; /**< XFF Header name */
+    HttpXFFCfg *xff_cfg;
 } Unified2AlertFileCtx;
 
 /**
@@ -328,85 +314,6 @@ static int Unified2Write(Unified2AlertThread *aun)
     return 1;
 }
 
-static int GetXFFIPFromTx(const Packet *p, uint64_t tx_id, char *xff_header, char *dstbuf, int dstbuflen)
-{
-    uint8_t xff_chain[UNIFIED2_ALERT_XFF_CHAIN_MAXLEN];
-    HtpState *htp_state = NULL;
-    htp_tx_t *tx = NULL;
-    uint64_t total_txs = 0;
-
-    htp_state = (HtpState *)FlowGetAppState(p->flow);
-
-    if (htp_state == NULL) {
-        SCLogDebug("no http state, XFF IP cannot be retrieved");
-        return 0;
-    }
-
-    total_txs = AppLayerParserGetTxCnt(p->flow->proto, ALPROTO_HTTP, htp_state);
-    if (tx_id >= total_txs)
-        return 0;
-
-    tx = AppLayerParserGetTx(p->flow->proto, ALPROTO_HTTP, htp_state, tx_id);
-    if (tx == NULL) {
-        SCLogDebug("tx is NULL, XFF cannot be retrieved");
-        return 0;
-    }
-
-    htp_header_t *h_xff = NULL;
-    if (tx->request_headers != NULL) {
-        h_xff = htp_table_get_c(tx->request_headers, xff_header);
-    }
-
-    if (h_xff != NULL && bstr_len(h_xff->value) >= UNIFIED2_ALERT_XFF_CHAIN_MINLEN &&
-            bstr_len(h_xff->value) < UNIFIED2_ALERT_XFF_CHAIN_MAXLEN) {
-
-        memcpy(xff_chain, bstr_ptr(h_xff->value), bstr_len(h_xff->value));
-        xff_chain[bstr_len(h_xff->value)]=0;
-        /** Check for chained IP's separated by ", ", we will get the last one */
-        uint8_t *p_xff = memrchr(xff_chain, ' ', bstr_len(h_xff->value));
-        if (p_xff == NULL) {
-            p_xff = xff_chain;
-        } else {
-            p_xff++;
-        }
-        /** Sanity check on extracted IP for IPv4 and IPv6 */
-        uint32_t ip[4];
-        if ( inet_pton(AF_INET, (char *)p_xff, ip ) == 1 ||
-                inet_pton(AF_INET6, (char *)p_xff, ip ) == 1 ) {
-            strlcpy(dstbuf, (char *)p_xff, dstbuflen);
-            return 1; // OK
-        }
-    }
-    return 0;
-}
-
-/**
- *  \brief Function to return XFF IP if any...
- *  \retval 1 if the IP has been found and returned in dstbuf
- *  \retval 0 if the IP has not being found or error
- */
-static int GetXFFIP(const Packet *p, char *xff_header, char *dstbuf, int dstbuflen)
-{
-    HtpState *htp_state = NULL;
-    uint64_t tx_id = 0;
-    uint64_t total_txs = 0;
-
-    htp_state = (HtpState *)FlowGetAppState(p->flow);
-    if (htp_state == NULL) {
-        SCLogDebug("no http state, XFF IP cannot be retrieved");
-        goto end;
-    }
-
-    total_txs = AppLayerParserGetTxCnt(p->flow->proto, ALPROTO_HTTP, htp_state);
-    for (; tx_id < total_txs; tx_id++) {
-        if (GetXFFIPFromTx(p, tx_id, xff_header, dstbuf, dstbuflen) == 1)
-            return 1;
-    }
-
-end:
-    return 0; // Not found
-}
-
 int Unified2Condition(ThreadVars *tv, const Packet *p) {
     if (likely(p->alerts.cnt == 0 && !(p->flags & PKT_HAS_TAG)))
         return FALSE;
@@ -423,39 +330,42 @@ int Unified2Logger(ThreadVars *t, void *data, const Packet *p)
 {
     int ret = 0;
     Unified2AlertThread *aun = (Unified2AlertThread *)data;
-    aun->xff_flags = UNIFIED2_ALERT_XFF_DISABLED;
+    aun->xff_flags = XFF_DISABLED;
+
+    HttpXFFCfg *xff_cfg = aun->unified2alert_ctx->xff_cfg;
 
     /* overwrite mode can only work per u2 block, not per individual
      * alert. So we'll look for an XFF record once */
-    if ((aun->unified2alert_ctx->xff_mode & UNIFIED2_ALERT_XFF_OVERWRITE) && p->flow != NULL) {
-        FLOWLOCK_RDLOCK(p->flow);
+    if ((xff_cfg->mode & XFF_OVERWRITE) && p->flow != NULL) {
+        char buffer[XFF_MAXLEN];
+        int have_xff_ip = 0;
 
+        FLOWLOCK_RDLOCK(p->flow);
         if (FlowGetAppProtocol(p->flow) == ALPROTO_HTTP) {
-            char buffer[UNIFIED2_ALERT_XFF_MAXLEN];
+            have_xff_ip = HttpXFFGetIP(p, xff_cfg->header, buffer, XFF_MAXLEN);
+        }
+        FLOWLOCK_UNLOCK(p->flow);
 
-            if (GetXFFIP(p, aun->unified2alert_ctx->xff_header, buffer, UNIFIED2_ALERT_XFF_MAXLEN) == 1) {
-                /** Be sure that we have a nice zeroed buffer */
-                memset(aun->xff_ip, 0, 4 * sizeof(uint32_t));
+        if (have_xff_ip) {
+            /** Be sure that we have a nice zeroed buffer */
+            memset(aun->xff_ip, 0, 4 * sizeof(uint32_t));
 
-                /** We can only have override mode if packet IP version matches
-                 * the XFF IP version, otherwise fall-back to extra data */
-                if (inet_pton(AF_INET, buffer, aun->xff_ip) == 1) {
-                    SCLogDebug("valid ipv4 xff, setting flags %s", buffer);
-                    if (PKT_IS_IPV4(p)) {
-                        aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV4|UNIFIED2_ALERT_XFF_OVERWRITE);
-                    } else {
-                        aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV4|UNIFIED2_ALERT_XFF_EXTRADATA);
-                    }
-                } else if (inet_pton(AF_INET6, buffer, aun->xff_ip) == 1) {
-                    if (PKT_IS_IPV6(p)) {
-                        aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV6|UNIFIED2_ALERT_XFF_OVERWRITE);
-                    } else {
-                        aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV6|UNIFIED2_ALERT_XFF_EXTRADATA);
-                    }
+            /** We can only have override mode if packet IP version matches
+             * the XFF IP version, otherwise fall-back to extra data */
+            if (inet_pton(AF_INET, buffer, aun->xff_ip) == 1) {
+                if (PKT_IS_IPV4(p)) {
+                    aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV4|XFF_OVERWRITE);
+                } else {
+                    aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV4|XFF_EXTRADATA);
+                }
+            } else if (inet_pton(AF_INET6, buffer, aun->xff_ip) == 1) {
+                if (PKT_IS_IPV6(p)) {
+                    aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV6|XFF_OVERWRITE);
+                } else {
+                    aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV6|XFF_EXTRADATA);
                 }
             }
         }
-        FLOWLOCK_UNLOCK(p->flow);
     }
 
     if (PKT_IS_IPV4(p)) {
@@ -555,7 +465,7 @@ static int Unified2PrintStreamSegmentCallback(const Packet *p, void *data, uint8
     aun->offset = 0;
 
     // If XFF is in extra data mode...
-    if (aun->xff_flags & UNIFIED2_ALERT_XFF_EXTRADATA) {
+    if (aun->xff_flags & XFF_EXTRADATA) {
         memset(dhdr, 0, sizeof(Unified2ExtraData));
 
         if (aun->xff_flags & UNIFIED2_ALERT_XFF_IPV4) {
@@ -661,8 +571,9 @@ static int Unified2PrintStreamSegmentCallback(const Packet *p, void *data, uint8
             goto error;
         }
         /** If XFF is in overwrite mode... */
-        if (aun->xff_flags & UNIFIED2_ALERT_XFF_OVERWRITE) {
+        if (aun->xff_flags & XFF_OVERWRITE) {
             BUG_ON(aun->xff_flags & UNIFIED2_ALERT_XFF_IPV6);
+
             if (p->flowflags & FLOW_PKT_TOCLIENT) {
                 fakehdr.ip4h.s_ip_dst.s_addr = aun->xff_ip[0];
             } else {
@@ -703,7 +614,7 @@ static int Unified2PrintStreamSegmentCallback(const Packet *p, void *data, uint8
             goto error;
         }
         /** If XFF is in overwrite mode... */
-        if (aun->xff_flags & UNIFIED2_ALERT_XFF_OVERWRITE) {
+        if (aun->xff_flags & XFF_OVERWRITE) {
             BUG_ON(aun->xff_flags & UNIFIED2_ALERT_XFF_IPV4);
 
             if (p->flowflags & FLOW_PKT_TOCLIENT) {
@@ -923,7 +834,7 @@ static int Unified2IPv6TypeAlert(ThreadVars *t, const Packet *p, void *data)
     gphdr.src_ip = *(struct in6_addr*)GET_IPV6_SRC_ADDR(p);
     gphdr.dst_ip = *(struct in6_addr*)GET_IPV6_DST_ADDR(p);
     /** If XFF is in overwrite mode... */
-    if (aun->xff_flags & UNIFIED2_ALERT_XFF_OVERWRITE) {
+    if (aun->xff_flags & XFF_OVERWRITE) {
         BUG_ON(aun->xff_flags & UNIFIED2_ALERT_XFF_IPV4);
 
         if (p->flowflags & FLOW_PKT_TOCLIENT) {
@@ -983,29 +894,31 @@ static int Unified2IPv6TypeAlert(ThreadVars *t, const Packet *p, void *data)
         if (unlikely(pa->s == NULL))
             continue;
 
-        if ((aun->unified2alert_ctx->xff_mode & UNIFIED2_ALERT_XFF_EXTRADATA) && p->flow != NULL) {
+        HttpXFFCfg *xff_cfg = aun->unified2alert_ctx->xff_cfg;
+
+        if ((xff_cfg->mode & XFF_EXTRADATA) && p->flow != NULL) {
+            char buffer[XFF_MAXLEN];
+            int have_xff_ip = 0;
+
             FLOWLOCK_RDLOCK(p->flow);
             if (FlowGetAppProtocol(p->flow) == ALPROTO_HTTP) {
-                char buffer[UNIFIED2_ALERT_XFF_MAXLEN];
-                int have_xff_ip = 0;
-
                 if (pa->flags & PACKET_ALERT_FLAG_TX) {
-                    have_xff_ip = GetXFFIPFromTx(p, pa->tx_id, aun->unified2alert_ctx->xff_header, buffer, UNIFIED2_ALERT_XFF_MAXLEN);
+                    have_xff_ip = HttpXFFGetIPFromTx(p, pa->tx_id, xff_cfg->header, buffer, XFF_MAXLEN);
                 } else {
-                    have_xff_ip = GetXFFIP(p, aun->unified2alert_ctx->xff_header, buffer, UNIFIED2_ALERT_XFF_MAXLEN);
-                }
-                if (have_xff_ip) {
-                    memset(aun->xff_ip, 0, 4 * sizeof(uint32_t));
-
-                    if (inet_pton(AF_INET, buffer, aun->xff_ip) == 1) {
-                        SCLogDebug("valid ipv4 xff, setting flag");
-                        aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV4|UNIFIED2_ALERT_XFF_EXTRADATA);
-                    } else if (inet_pton(AF_INET6, buffer, aun->xff_ip) == 1) {
-                        aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV6|UNIFIED2_ALERT_XFF_EXTRADATA);
-                    }
+                    have_xff_ip = HttpXFFGetIP(p, xff_cfg->header, buffer, XFF_MAXLEN);
                 }
             }
             FLOWLOCK_UNLOCK(p->flow);
+
+            if (have_xff_ip) {
+                memset(aun->xff_ip, 0, 4 * sizeof(uint32_t));
+
+                if (inet_pton(AF_INET, buffer, aun->xff_ip) == 1) {
+                    aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV4|XFF_EXTRADATA);
+                } else if (inet_pton(AF_INET6, buffer, aun->xff_ip) == 1) {
+                    aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV6|XFF_EXTRADATA);
+                }
+            }
         }
 
         /* reset length and offset */
@@ -1108,7 +1021,7 @@ static int Unified2IPv4TypeAlert (ThreadVars *tv, const Packet *p, void *data)
     gphdr.src_ip = p->ip4h->s_ip_src.s_addr;
     gphdr.dst_ip = p->ip4h->s_ip_dst.s_addr;
     /** If XFF is in overwrite mode... */
-    if (aun->xff_flags & UNIFIED2_ALERT_XFF_OVERWRITE) {
+    if (aun->xff_flags & XFF_OVERWRITE) {
         BUG_ON(aun->xff_flags & UNIFIED2_ALERT_XFF_IPV6);
 
         if (p->flowflags & FLOW_PKT_TOCLIENT) {
@@ -1158,30 +1071,31 @@ static int Unified2IPv4TypeAlert (ThreadVars *tv, const Packet *p, void *data)
         if (unlikely(pa->s == NULL))
             continue;
 
-        if ((aun->unified2alert_ctx->xff_mode & UNIFIED2_ALERT_XFF_EXTRADATA) && p->flow != NULL) {
+        HttpXFFCfg *xff_cfg = aun->unified2alert_ctx->xff_cfg;
+
+        if ((xff_cfg->mode & XFF_EXTRADATA) && p->flow != NULL) {
+            char buffer[XFF_MAXLEN];
+            int have_xff_ip = 0;
+
             FLOWLOCK_RDLOCK(p->flow);
             if (FlowGetAppProtocol(p->flow) == ALPROTO_HTTP) {
-                char buffer[UNIFIED2_ALERT_XFF_MAXLEN];
-                int have_xff_ip = 0;
-
                 if (pa->flags & PACKET_ALERT_FLAG_TX) {
-                    have_xff_ip = GetXFFIPFromTx(p, pa->tx_id, aun->unified2alert_ctx->xff_header, buffer, UNIFIED2_ALERT_XFF_MAXLEN);
+                    have_xff_ip = HttpXFFGetIPFromTx(p, pa->tx_id, xff_cfg->header, buffer, XFF_MAXLEN);
                 } else {
-                    have_xff_ip = GetXFFIP(p, aun->unified2alert_ctx->xff_header, buffer, UNIFIED2_ALERT_XFF_MAXLEN);
-                }
-                if (have_xff_ip) {
-                    SCLogDebug("buffer %s", buffer);
-                    memset(aun->xff_ip, 0, 4 * sizeof(uint32_t));
-
-                    if (inet_pton(AF_INET, buffer, aun->xff_ip) == 1) {
-                        SCLogDebug("valid ipv4 xff, setting flags %s", buffer);
-                        aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV4|UNIFIED2_ALERT_XFF_EXTRADATA);
-                    } else if (inet_pton(AF_INET6, buffer, aun->xff_ip) == 1) {
-                        aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV6|UNIFIED2_ALERT_XFF_EXTRADATA);
-                    }
+                    have_xff_ip = HttpXFFGetIP(p, xff_cfg->header, buffer, XFF_MAXLEN);
                 }
             }
             FLOWLOCK_UNLOCK(p->flow);
+
+            if (have_xff_ip) {
+                memset(aun->xff_ip, 0, 4 * sizeof(uint32_t));
+
+                if (inet_pton(AF_INET, buffer, aun->xff_ip) == 1) {
+                    aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV4|XFF_EXTRADATA);
+                } else if (inet_pton(AF_INET6, buffer, aun->xff_ip) == 1) {
+                    aun->xff_flags = (UNIFIED2_ALERT_XFF_IPV6|XFF_EXTRADATA);
+                }
+            }
         }
 
         /* reset length and offset */
@@ -1331,7 +1245,7 @@ OutputCtx *Unified2AlertInitCtx(ConfNode *conf)
     int ret = 0;
     LogFileCtx* file_ctx = NULL;
     OutputCtx* output_ctx = NULL;
-    ConfNode *xff_node = NULL;
+    HttpXFFCfg *xff_cfg = NULL;
 
     file_ctx = LogFileNewCtx();
     if (file_ctx == NULL) {
@@ -1395,49 +1309,27 @@ OutputCtx *Unified2AlertInitCtx(ConfNode *conf)
     if (unlikely(output_ctx == NULL))
         goto error;
 
+    xff_cfg = SCMalloc(sizeof(HttpXFFCfg));
+    if (unlikely(xff_cfg == NULL)) {
+        goto error;
+    }
+    memset(xff_cfg, 0x00, sizeof(HttpXFFCfg));
+
+    if (conf != NULL) {
+        HttpXFFGetCfg(conf, xff_cfg);
+    }
+
     Unified2AlertFileCtx *unified2alert_ctx = SCMalloc(sizeof(Unified2AlertFileCtx));
     if (unlikely(unified2alert_ctx == NULL)) {
         goto error;
     }
     memset(unified2alert_ctx, 0x00, sizeof(Unified2AlertFileCtx));
+
     unified2alert_ctx->file_ctx = file_ctx;
+    unified2alert_ctx->xff_cfg = xff_cfg;
     output_ctx->data = unified2alert_ctx;
-
     output_ctx->DeInit = Unified2AlertDeInitCtx;
 
-    if (conf != NULL)
-        xff_node = ConfNodeLookupChild(conf, "xff");
-
-    if (xff_node != NULL && ConfNodeChildValueIsTrue(xff_node, "enabled")) {
-        const char *xff_mode = ConfNodeLookupChildValue(xff_node, "mode");
-
-        if (xff_mode != NULL && strcasecmp(xff_mode, "overwrite") == 0) {
-                unified2alert_ctx->xff_mode |= UNIFIED2_ALERT_XFF_OVERWRITE;
-        } else {
-            if (xff_mode == NULL) {
-                SCLogWarning(SC_WARN_XFF_INVALID_MODE, "The unified2 output XFF mode hasn't been defined, falling back to extra-data mode");
-            }
-            else if (strcasecmp(xff_mode, "extra-data") != 0) {
-                SCLogWarning(SC_WARN_XFF_INVALID_MODE, "The unified2 output XFF mode %s is invalid, falling back to extra-data mode",
-                        xff_mode);
-            }
-            unified2alert_ctx->xff_mode |= UNIFIED2_ALERT_XFF_EXTRADATA;
-        }
-
-        const char *xff_header = ConfNodeLookupChildValue(xff_node, "header");
-
-        if (xff_header != NULL) {
-            unified2alert_ctx->xff_header = (char *) xff_header;
-        } else {
-            SCLogWarning(SC_WARN_XFF_INVALID_HEADER, "The unified2 output XFF header hasn't been defined, using the default %s",
-                    UNIFIED2_ALERT_XFF_DEFAULT);
-            unified2alert_ctx->xff_header = UNIFIED2_ALERT_XFF_DEFAULT;
-        }
-    }
-    else {
-        unified2alert_ctx->xff_mode = UNIFIED2_ALERT_XFF_DISABLED;
-    }
-
     SCLogInfo("Unified2-alert initialized: filename %s, limit %"PRIu64" MB",
               filename, file_ctx->size_limit / (1024*1024));
 
@@ -1446,6 +1338,9 @@ OutputCtx *Unified2AlertInitCtx(ConfNode *conf)
     return output_ctx;
 
 error:
+    if (xff_cfg != NULL) {
+        SCFree(xff_cfg);
+    }
     if (output_ctx != NULL) {
         SCFree(output_ctx);
     }
@@ -1462,6 +1357,10 @@ static void Unified2AlertDeInitCtx(OutputCtx *output_ctx)
             if (logfile_ctx != NULL) {
                 LogFileFreeCtx(logfile_ctx);
             }
+            HttpXFFCfg *xff_cfg = unified2alert_ctx->xff_cfg;
+            if (xff_cfg != NULL) {
+                SCFree(xff_cfg);
+            }
             SCFree(unified2alert_ctx);
         }
         SCFree(output_ctx);
diff --git a/src/app-layer-htp-xff.c b/src/app-layer-htp-xff.c
new file mode 100644 (file)
index 0000000..2abf972
--- /dev/null
@@ -0,0 +1,168 @@
+/* Copyright (C) 2014 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 Ignacio Sanchez <sanchezmartin.ji@gmail.com>
+ * \author Duarte Silva <duarte.silva@serializing.me>
+ */
+
+#include "suricata-common.h"
+#include "conf.h"
+
+#include "app-layer-parser.h"
+#include "app-layer-htp.h"
+#include "app-layer-htp-xff.h"
+
+#include "util-misc.h"
+
+/** XFF header value minimal length */
+#define XFF_CHAIN_MINLEN 7
+/** XFF header value maximum length */
+#define XFF_CHAIN_MAXLEN 256
+/** Default XFF header name */
+#define XFF_DEFAULT "X-Forwarded-For"
+
+/**
+ * \brief Function to return XFF IP if any in the selected transaction. The
+ * caller needs to lock the flow.
+ * \retval 1 if the IP has been found and returned in dstbuf
+ * \retval 0 if the IP has not being found or error
+ */
+int HttpXFFGetIPFromTx(const Packet *p, uint64_t tx_id, char *xff_header, char *dstbuf,
+        int dstbuflen)
+{
+    uint8_t xff_chain[XFF_CHAIN_MAXLEN];
+    HtpState *htp_state = NULL;
+    htp_tx_t *tx = NULL;
+    uint64_t total_txs = 0;
+
+    htp_state = (HtpState *)FlowGetAppState(p->flow);
+
+    if (htp_state == NULL) {
+        SCLogDebug("no http state, XFF IP cannot be retrieved");
+        return 0;
+    }
+
+    total_txs = AppLayerParserGetTxCnt(p->flow->proto, ALPROTO_HTTP, htp_state);
+    if (tx_id >= total_txs)
+        return 0;
+
+    tx = AppLayerParserGetTx(p->flow->proto, ALPROTO_HTTP, htp_state, tx_id);
+    if (tx == NULL) {
+        SCLogDebug("tx is NULL, XFF cannot be retrieved");
+        return 0;
+    }
+
+    htp_header_t *h_xff = NULL;
+    if (tx->request_headers != NULL) {
+        h_xff = htp_table_get_c(tx->request_headers, xff_header);
+    }
+
+    if (h_xff != NULL && bstr_len(h_xff->value) >= XFF_CHAIN_MINLEN &&
+            bstr_len(h_xff->value) < XFF_CHAIN_MAXLEN) {
+
+        memcpy(xff_chain, bstr_ptr(h_xff->value), bstr_len(h_xff->value));
+        xff_chain[bstr_len(h_xff->value)]=0;
+        /** Check for chained IP's separated by ", ", we will get the last one */
+        uint8_t *p_xff = memrchr(xff_chain, ' ', bstr_len(h_xff->value));
+        if (p_xff == NULL) {
+            p_xff = xff_chain;
+        } else {
+            p_xff++;
+        }
+        /** Sanity check on extracted IP for IPv4 and IPv6 */
+        uint32_t ip[4];
+        if ( inet_pton(AF_INET, (char *)p_xff, ip ) == 1 ||
+                inet_pton(AF_INET6, (char *)p_xff, ip ) == 1 ) {
+            strlcpy(dstbuf, (char *)p_xff, dstbuflen);
+            return 1; // OK
+        }
+    }
+    return 0;
+}
+
+/**
+ *  \brief Function to return XFF IP if any. The caller needs to lock the flow.
+ *  \retval 1 if the IP has been found and returned in dstbuf
+ *  \retval 0 if the IP has not being found or error
+ */
+int HttpXFFGetIP(const Packet *p, char *xff_header, char *dstbuf, int dstbuflen)
+{
+    HtpState *htp_state = NULL;
+    uint64_t tx_id = 0;
+    uint64_t total_txs = 0;
+
+    htp_state = (HtpState *)FlowGetAppState(p->flow);
+    if (htp_state == NULL) {
+        SCLogDebug("no http state, XFF IP cannot be retrieved");
+        goto end;
+    }
+
+    total_txs = AppLayerParserGetTxCnt(p->flow->proto, ALPROTO_HTTP, htp_state);
+    for (; tx_id < total_txs; tx_id++) {
+        if (HttpXFFGetIPFromTx(p, tx_id, xff_header, dstbuf, dstbuflen) == 1)
+            return 1;
+    }
+
+end:
+    return 0; // Not found
+}
+
+/**
+ * \brief Function to return XFF configuration from a configuration node.
+ */
+void HttpXFFGetCfg(ConfNode *conf, HttpXFFCfg *result)
+{
+    BUG_ON(conf == NULL || result == NULL);
+
+    ConfNode *xff_node = NULL;
+
+    if (conf != NULL)
+        xff_node = ConfNodeLookupChild(conf, "xff");
+
+    if (xff_node != NULL && ConfNodeChildValueIsTrue(xff_node, "enabled")) {
+        const char *xff_mode = ConfNodeLookupChildValue(xff_node, "mode");
+
+        if (xff_mode != NULL && strcasecmp(xff_mode, "overwrite") == 0) {
+            result->mode |= XFF_OVERWRITE;
+        } else {
+            if (xff_mode == NULL) {
+                SCLogWarning(SC_WARN_XFF_INVALID_MODE, "The XFF mode hasn't been defined, falling back to extra-data mode");
+            }
+            else if (strcasecmp(xff_mode, "extra-data") != 0) {
+                SCLogWarning(SC_WARN_XFF_INVALID_MODE, "The XFF mode %s is invalid, falling back to extra-data mode",
+                        xff_mode);
+            }
+            result->mode |= XFF_EXTRADATA;
+        }
+
+        const char *xff_header = ConfNodeLookupChildValue(xff_node, "header");
+
+        if (xff_header != NULL) {
+            result->header = (char *) xff_header;
+        } else {
+            SCLogWarning(SC_WARN_XFF_INVALID_HEADER, "The XFF header hasn't been defined, using the default %s",
+                    XFF_DEFAULT);
+            result->header = XFF_DEFAULT;
+        }
+    }
+    else {
+        result->mode = XFF_DISABLED;
+    }
+}
diff --git a/src/app-layer-htp-xff.h b/src/app-layer-htp-xff.h
new file mode 100644 (file)
index 0000000..45c74b7
--- /dev/null
@@ -0,0 +1,48 @@
+/* Copyright (C) 2014 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 Ignacio Sanchez <sanchezmartin.ji@gmail.com>
+ * \author Duarte Silva <duarte.silva@serializing.me>
+ */
+
+#ifndef __APP_LAYER_HTP_XFF_H__
+#define __APP_LAYER_HTP_XFF_H__
+
+/** XFF is disabled */
+#define XFF_DISABLED 1
+/** XFF extra data mode */
+#define XFF_EXTRADATA 2
+/** XFF overwrite mode */
+#define XFF_OVERWRITE 4
+/** Single XFF IP maximum length (default value based on IPv6 address length) */
+#define XFF_MAXLEN 46
+
+typedef struct HttpXFFCfg_ {
+    uint8_t mode; /**< XFF operation mode */
+    char *header; /**< XFF header name */
+} HttpXFFCfg;
+
+void HttpXFFGetCfg(ConfNode *conf, HttpXFFCfg *result);
+
+int HttpXFFGetIPFromTx(const Packet *p, uint64_t tx_id, char *xff_header, char *dstbuf, int dstbuflen);
+
+int HttpXFFGetIP(const Packet *p, char *xff_header, char *dstbuf, int dstbuflen);
+
+#endif /* __APP_LAYER_HTP_XFF_H__ */
index d4d9f72d43d79b14f2b66f9a4573fc0edc48b223..266e96c7b00e108f2482d2d2c220ab4d5f1b0b9c 100644 (file)
@@ -44,6 +44,8 @@
 #include "detect-engine-mpm.h"
 #include "detect-reference.h"
 #include "app-layer-parser.h"
+#include "app-layer-htp.h"
+#include "app-layer-htp-xff.h"
 #include "util-classification-config.h"
 #include "util-syslog.h"
 
@@ -74,6 +76,7 @@
 typedef struct AlertJsonOutputCtx_ {
     LogFileCtx* file_ctx;
     uint8_t flags;
+    HttpXFFCfg *xff_cfg;
 } AlertJsonOutputCtx;
 
 typedef struct JsonAlertLogThread_ {
@@ -250,6 +253,37 @@ static int AlertJson(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p)
             json_object_set_new(js, "packet", json_string((char *)encoded_packet));
         }
 
+        HttpXFFCfg *xff_cfg = json_output_ctx->xff_cfg;
+
+        /* xff header */
+        if (!(xff_cfg->mode & XFF_DISABLED) && p->flow != NULL) {
+            int have_xff_ip = 0;
+            char buffer[XFF_MAXLEN];
+
+            FLOWLOCK_RDLOCK(p->flow);
+            if (FlowGetAppProtocol(p->flow) == ALPROTO_HTTP) {
+                if (pa->flags & PACKET_ALERT_FLAG_TX) {
+                    have_xff_ip = HttpXFFGetIPFromTx(p, pa->tx_id, xff_cfg->header, buffer, XFF_MAXLEN);
+                } else {
+                    have_xff_ip = HttpXFFGetIP(p, xff_cfg->header, buffer, XFF_MAXLEN);
+                }
+            }
+            FLOWLOCK_UNLOCK(p->flow);
+
+            if (have_xff_ip) {
+                if (xff_cfg->mode & XFF_EXTRADATA) {
+                    json_object_set_new(js, "xff", json_string(buffer));
+                }
+                else if (xff_cfg->mode & XFF_OVERWRITE) {
+                    if (p->flowflags & FLOW_PKT_TOCLIENT) {
+                        json_object_set(js, "dest_ip", json_string(buffer));
+                    } else {
+                        json_object_set(js, "src_ip", json_string(buffer));
+                    }
+                }
+            }
+        }
+
         OutputJSONBuffer(js, aft->file_ctx, aft->json_buffer);
         json_object_del(js, "alert");
     }
@@ -413,6 +447,11 @@ static void JsonAlertLogDeInitCtxSub(OutputCtx *output_ctx)
     AlertJsonOutputCtx *json_output_ctx = (AlertJsonOutputCtx *) output_ctx->data;
 
     if (json_output_ctx != NULL) {
+        HttpXFFCfg *xff_cfg = json_output_ctx->xff_cfg;
+        if (xff_cfg != NULL) {
+            SCFree(xff_cfg);
+        }
+
         SCFree(json_output_ctx);
     }
     SCFree(output_ctx);
@@ -455,19 +494,27 @@ static OutputCtx *JsonAlertLogInitCtx(ConfNode *conf)
 static OutputCtx *JsonAlertLogInitCtxSub(ConfNode *conf, OutputCtx *parent_ctx)
 {
     AlertJsonThread *ajt = parent_ctx->data;
+    AlertJsonOutputCtx *json_output_ctx = NULL;
+    HttpXFFCfg *xff_cfg = NULL;
 
     OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
     if (unlikely(output_ctx == NULL))
         return NULL;
 
-    AlertJsonOutputCtx *json_output_ctx = SCMalloc(sizeof(AlertJsonOutputCtx));
+    json_output_ctx = SCMalloc(sizeof(AlertJsonOutputCtx));
     if (unlikely(json_output_ctx == NULL)) {
-        SCFree(output_ctx);
-        return NULL;
+        goto error;
     }
-
     memset(json_output_ctx, 0, sizeof(AlertJsonOutputCtx));
+
+    xff_cfg = SCMalloc(sizeof(HttpXFFCfg));
+    if (unlikely(xff_cfg == NULL)) {
+        goto error;
+    }
+    memset(xff_cfg, 0, sizeof(HttpXFFCfg));
+
     json_output_ctx->file_ctx = ajt->file_ctx;
+    json_output_ctx->xff_cfg = xff_cfg;
 
     if (conf != NULL) {
         const char *payload = ConfNodeLookupChildValue(conf, "payload");
@@ -495,12 +542,24 @@ static OutputCtx *JsonAlertLogInitCtxSub(ConfNode *conf, OutputCtx *parent_ctx)
                 json_output_ctx->flags |= LOG_JSON_PACKET;
             }
         }
+
+        HttpXFFGetCfg(conf, xff_cfg);
     }
 
     output_ctx->data = json_output_ctx;
     output_ctx->DeInit = JsonAlertLogDeInitCtxSub;
 
     return output_ctx;
+
+error:
+    if (json_output_ctx != NULL) {
+        SCFree(json_output_ctx);
+    }
+    if (output_ctx != NULL) {
+        SCFree(output_ctx);
+    }
+
+    return NULL;
 }
 
 void TmModuleJsonAlertLogRegister (void)
index c9458e733da61ecde411f166b1eca46cd21debf9..dc4b9babd8573b695dab1b06d56de057e8bec476 100644 (file)
@@ -105,6 +105,20 @@ outputs:
             # payload-printable: yes # enable dumping payload in printable (lossy) format
             # packet: yes            # enable dumping of packet (without stream segments)
             # http: yes              # enable dumping of http fields
+
+            # HTTP X-Forwarded-For support by adding an extra field or overwriting
+            # the source or destination IP address (depending on flow direction)
+            # with the one reported in the X-Forwarded-For HTTP header. This is
+            # helpful when reviewing alerts for traffic that is being reversed
+            # proxied.
+            xff:
+              enabled: no
+              # Two operation modes are available, "extra-data" and "overwrite".
+              mode: extra-data
+              # Header name where the actual IP address will be reported, if more
+              # than one IP address is present, the last IP address will be the
+              # one taken into consideration.
+              header: X-Forwarded-For
         - http:
             extended: yes     # enable this for extended logging information
             # custom allows additional http fields to be included in eve-log
@@ -136,10 +150,11 @@ outputs:
       # Sensor ID field of unified2 alerts.
       #sensor-id: 0
 
-      # HTTP X-Forwarded-For support by adding the unified2 extra header that
-      # will contain the actual client IP address or by overwriting the source
-      # IP address (helpful when inspecting traffic that is being reversed
-      # proxied).
+      # HTTP X-Forwarded-For support by adding the unified2 extra header or
+      # overwriting the source or destination IP address (depending on flow
+      # direction) with the one reported in the X-Forwarded-For HTTP header.
+      # This is helpful when reviewing alerts for traffic that is being reversed
+      # proxied.
       xff:
         enabled: no
         # Two operation modes are available, "extra-data" and "overwrite". Note
@@ -147,10 +162,10 @@ outputs:
         # X-Forwarded-For header is of a different version of the packet
         # received, it will fall-back to "extra-data" mode.
         mode: extra-data
-        # Header name were the actual IP address will be reported, if more than
-        # one IP address is present, the last IP address will be the one taken
-        # into consideration.
-        header: X-Forwarded-For 
+        # Header name where the actual IP address will be reported, if more
+        # than one IP address is present, the last IP address will be the
+        # one taken into consideration.
+        header: X-Forwarded-For
 
   # a line based log of HTTP requests (no alerts)
   - http-log: