]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
Lua: SMTP support; Addresses feature ticket #1775; v5
authortobiass1 <130472@hig.no>
Wed, 15 Jun 2016 02:28:50 +0000 (22:28 -0400)
committerVictor Julien <victor@inliniac.net>
Tue, 5 Jul 2016 14:09:50 +0000 (16:09 +0200)
src/Makefile.am
src/detect-lua-extensions.c
src/util-lua-smtp.c [new file with mode: 0644]
src/util-lua-smtp.h [new file with mode: 0644]

index 65f175e8c512a9b7a568ce05ec2da188ab4ba77a..be200cb6289aa1f63bcf2b98567cf04a22f4a930 100644 (file)
@@ -364,6 +364,7 @@ util-lua-dns.c util-lua-dns.h \
 util-lua-http.c util-lua-http.h \
 util-lua-tls.c util-lua-tls.h \
 util-lua-ssh.c util-lua-ssh.h \
+util-lua-smtp.c util-lua-smtp.h \
 util-magic.c util-magic.h \
 util-memcmp.c util-memcmp.h \
 util-memcpy.h \
index ae9b4e141272ae7e5498f9863029ff42b37d1351..44a8cfd216e193e27c2ddff6b3d0178ccd624829 100644 (file)
@@ -68,6 +68,7 @@
 #include "util-lua-dns.h"
 #include "util-lua-tls.h"
 #include "util-lua-ssh.h"
+#include "util-lua-smtp.h"
 
 static const char luaext_key_ld[] = "suricata:luajitdata";
 static const char luaext_key_det_ctx[] = "suricata:det_ctx";
@@ -621,6 +622,7 @@ int LuaRegisterExtensions(lua_State *lua_state)
     LuaRegisterDnsFunctions(lua_state);
     LuaRegisterTlsFunctions(lua_state);
     LuaRegisterSshFunctions(lua_state);
+    LuaRegisterSmtpFunctions(lua_state);
     return 0;
 }
 
diff --git a/src/util-lua-smtp.c b/src/util-lua-smtp.c
new file mode 100644 (file)
index 0000000..1d0e4ed
--- /dev/null
@@ -0,0 +1,374 @@
+/* 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 casec Bachelors group
+ *  \author Lauritz Prag Sømme <lauritz24@me.com>
+ *  \author Levi Tobiassen <levi.tobiassen@gmail.com>
+ *  \author Stian Hoel Bergseth <stian.bergseth@hig.no>
+ *  \author Vinjar Hillestad <vinjar.hillestad@hig.no>
+ */
+
+#include "suricata-common.h"
+
+#include "debug.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+#include "output.h"
+
+#include "app-layer-smtp.h"
+
+#ifdef HAVE_LUA
+
+#include <lua.h>
+#include <lualib.h>
+
+#include "util-lua.h"
+#include "util-lua-common.h"
+#include "util-file.h"
+
+/*
+ * \brief internal function used by SMTPGetMimeField
+ *
+ * \param luastate luastate stack to use and push attributes to
+ * \param flow network flow of SMTP packets
+ * \param name name of the attribute to extract from MimeDecField
+ *
+ * \retval 1 if success mimefield found and pushed to stack. Returns error
+ * int and msg pushed to luastate stack if error occurs.
+ */
+
+static int GetMimeDecField(lua_State *luastate, Flow *flow, const char *name)
+{
+    /* extract state from flow */
+    SMTPState *state = (SMTPState *) FlowGetAppState(flow);
+    /* check that state exsists */
+    if(state == NULL) {
+        return LuaCallbackError(luastate, "Internal error: no state in flow");
+    }
+    /* pointer to current transaction in state */
+    SMTPTransaction *smtp_tx = state->curr_tx;
+    if(smtp_tx == NULL) {
+        return LuaCallbackError(luastate, "Transaction ending or not found");
+    }
+    /* pointer to tail of msg list of MimeDecEntitys in current transaction. */
+    MimeDecEntity *mime = smtp_tx->msg_tail;
+    /* check if msg_tail was hit */
+    if(mime == NULL){
+        return LuaCallbackError(luastate, "Internal error: no fields in transaction");
+    }
+    /* extract MIME field based on spesific field name. */
+    MimeDecField *field = MimeDecFindField(mime, name);
+    /* check MIME field */
+    if(field == NULL) {
+        return LuaCallbackError(luastate, "Error: mimefield not found");
+    }
+    /* return extracted field. */
+    if(field->value == NULL || field->value_len == 0){
+        return LuaCallbackError(luastate, "Error, pointer error");
+    }
+
+    return LuaPushStringBuffer(luastate, field->value, field->value_len);
+}
+
+/**
+ * \brief Function extracts specific MIME field based on argument from luastate
+ * stack then pushing the attribute onto the luastate stack.
+ *
+ * \param luastate luastate stack to pop and push attributes for I/O to lua
+ *
+ * \retval 1 if success mimefield found and pushed to stack. Returns error
+ * int and msg pushed to luastate stack if error occurs.
+ */
+
+static int SMTPGetMimeField(lua_State *luastate)
+{
+    if(!(LuaStateNeedProto(luastate, ALPROTO_SMTP))) {
+        return LuaCallbackError(luastate, "error: protocol not SMTP");
+    }
+    int lock_hint = 0;
+    Flow *flow = LuaStateGetFlow(luastate, &lock_hint);
+    /* check that flow exist */
+    if(flow == NULL) {
+        return LuaCallbackError(luastate, "Error: no flow found");
+    }
+    const char *name = LuaGetStringArgument(luastate, 1);
+    /* lock check */
+    if(lock_hint == LUA_FLOW_NOT_LOCKED_BY_PARENT) {
+        FLOWLOCK_RDLOCK(flow);
+        /* get specific MIME field */
+        GetMimeDecField(luastate, flow, name);
+        /* unlock flow mutex to allow for multithreading */
+        FLOWLOCK_UNLOCK(flow);
+        /* return number of fields pushed to luastate */
+    } else { /* if mutex already locked */
+        GetMimeDecField(luastate, flow, name);
+    }
+    return 1;
+}
+
+/**
+ * \brief Internal function used by SMTPGetMimeList
+ *
+ * \param luastate luastate stack to pop and push attributes for I/O to lua
+ * \param flow network flow of SMTP packets
+ *
+ * \retval 1 if the mimelist table is pushed to luastate stack.
+ * Returns error int and msg pushed to luastate stack if error occurs.
+*/
+
+static int GetMimeList(lua_State *luastate, Flow *flow)
+{
+
+    SMTPState *state = (SMTPState *) FlowGetAppState(flow);
+    if(state == NULL) {
+        return LuaCallbackError(luastate, "Error: no SMTP state");
+    }
+    /* Create a pointer to the current SMTPtransaction */
+    SMTPTransaction *smtp_tx = state->curr_tx;
+    if(smtp_tx == NULL) {
+        return LuaCallbackError(luastate, "Error: no SMTP transaction found");
+    }
+    /* Create a pointer to the tail of MimeDecEntity list */
+    MimeDecEntity *mime = smtp_tx->msg_tail;
+    if(mime == NULL) {
+        return LuaCallbackError(luastate, "Error: no mime entity found");
+    }
+    MimeDecField *field = mime->field_list;
+    if(field == NULL) {
+        return LuaCallbackError(luastate, "Error: no field_list found");
+    }
+    if(field->name == NULL || field->name_len == 0) {
+        return LuaCallbackError(luastate, "Error: field has no name");
+    }
+    /* Counter of MIME fields found */
+    int num = 1;
+    /* loop trough the list of mimeFields, printing each name found */
+    lua_newtable(luastate);
+    while (field != NULL) {
+        if(field->name != NULL && field->name_len != 0) {
+            lua_pushinteger(luastate,num++);
+            LuaPushStringBuffer(luastate, field->name, field->name_len);
+            lua_settable(luastate,-3);
+        }
+        field = field->next;
+    }
+    return 1;
+}
+
+/**
+ * \brief Lists name and value to all MIME fields which
+ * is included in a SMTP transaction.
+ *
+ * \param luastate luastate stack to pop and push attributes for I/O to lua.
+ *
+ * \retval 1 if the table is pushed to lua.
+ * Returns error int and msg pushed to luastate stack if error occurs
+ *
+ */
+
+static int SMTPGetMimeList(lua_State *luastate)
+{
+    /* Check if right protocol */
+    if(!(LuaStateNeedProto(luastate, ALPROTO_SMTP))) {
+        return LuaCallbackError(luastate, "Error: protocol not SMTP");
+    }
+    /* mutex lock indicator var */
+    int lock_hint = 0;
+    /* Extract network flow */
+    Flow *flow = LuaStateGetFlow(luastate, &lock_hint);
+    if(flow == NULL) {
+        return LuaCallbackError(luastate, "Error: no flow found");
+    }
+    /* check if flow already locked */
+    if(lock_hint == LUA_FLOW_NOT_LOCKED_BY_PARENT) {
+        /* mutexlock flow */
+        FLOWLOCK_RDLOCK(flow);
+        GetMimeList(luastate, flow);
+        FLOWLOCK_UNLOCK(flow);
+    } else {
+        GetMimeList(luastate, flow);
+    }
+    return 1;
+}
+
+/**
+ * \brief internal function used by SMTPGetMailFrom
+ *
+ * \param luastate luastate stack to pop and push attributes for I/O to lua.
+ * \param flow flow to get state for SMTP
+ *
+ * \retval 1 if mailfrom field found.
+ * Retruns error int and msg pushed to luastate stack if error occurs
+ */
+
+static int GetMailFrom(lua_State *luastate, Flow *flow)
+{
+    /* Extract SMTPstate from current flow */
+    SMTPState *state = (SMTPState *) FlowGetAppState(flow);
+
+    if(state == NULL) {
+        return LuaCallbackError(luastate, "Internal Error: no state");
+    }
+    SMTPTransaction *smtp_tx = state->curr_tx;
+    if(smtp_tx == NULL) {
+        return LuaCallbackError(luastate, "Internal Error: no SMTP transaction");
+    }
+    if(smtp_tx->mail_from == NULL || smtp_tx->mail_from_len == 0) {
+        return LuaCallbackError(luastate, "MailFrom not found");
+    }
+    return LuaPushStringBuffer(luastate, smtp_tx->mail_from, smtp_tx->mail_from_len);
+    /* Returns 1 because we never push more then 1 item to the lua stack */
+}
+
+/**
+ * \brief Extracts mail_from parameter from SMTPState.
+ * Attribute may also be available from mimefields, although there is no
+ * guarantee of it existing as mime.
+ *
+ * \param luastate luastate stack to pop and push attributes for I/O to lua.
+ *
+ * \retval 1 if mailfrom field found.
+ * Retruns error int and msg pushed to luastate stack if error occurs
+ */
+
+static int SMTPGetMailFrom(lua_State *luastate)
+{
+    /* check protocol */
+    if(!(LuaStateNeedProto(luastate, ALPROTO_SMTP))) {
+        return LuaCallbackError(luastate, "Error: protocol not SMTP");
+    }
+    /* use lock_hint to check for mutexlock on flow */
+    int lock_hint = 0;
+    /* Extract flow, with lockhint to check mutexlocking */
+    Flow *flow = LuaStateGetFlow(luastate, &lock_hint);
+    if(flow == NULL) {
+        return LuaCallbackError(luastate, "Internal Error: no flow");
+    }
+    /* check if already mutexlocked by parents */
+    if(lock_hint == LUA_FLOW_NOT_LOCKED_BY_PARENT) {
+        /* mutexlock flow */
+        FLOWLOCK_RDLOCK(flow);
+        GetMailFrom(luastate, flow);
+        FLOWLOCK_UNLOCK(flow);
+    } else {
+        GetMailFrom(luastate, flow);
+    }
+    return 1;
+}
+
+/**
+ * \brief intern function used by SMTPGetRcpList
+ *
+ * \params luastate luastate stack for internal communication with Lua.
+ * Used to hand over data to the recieveing luascript.
+ *
+ * \retval 1 if the table is pushed to lua.
+ * Returns error int and msg pushed to luastate stack if error occurs
+ */
+
+static int GetRcptList(lua_State *luastate, Flow *flow)
+{
+
+    SMTPState *state = (SMTPState *) FlowGetAppState(flow);
+    if(state == NULL) {
+        return LuaCallbackError(luastate, "Internal error, no state");
+    }
+
+    SMTPTransaction *smtp_tx = state->curr_tx;
+    if(smtp_tx == NULL) {
+        return LuaCallbackError(luastate, "No more tx, or tx not found");
+    }
+
+    /* Create a new table in luastate for rcpt list */
+    lua_newtable(luastate);
+    /* rcpt var for iterator */
+    int u = 1;
+    SMTPString *rcpt;
+
+    TAILQ_FOREACH(rcpt, &smtp_tx->rcpt_to_list, next) {
+        LuaPushStringBuffer(luastate, rcpt->str, rcpt->len);
+        lua_pushinteger(luastate, u++);
+        lua_settable(luastate, -3);
+    }
+    /* return 1 since we allways push one table to luastate */
+    return 1;
+}
+
+/**
+ * \brief function loops through rcpt-list located in
+ * flow->SMTPState->SMTPTransaction, adding all items to a table.
+ * Then pushing it to the luastate stack.
+ *
+ * \params luastate luastate stack for internal communication with Lua.
+ * Used to hand over data to the recieveing luascript.
+ *
+ * \retval 1 if the table is pushed to lua.
+ * Returns error int and msg pushed to luastate stack if error occurs
+ */
+
+static int SMTPGetRcptList(lua_State *luastate)
+{
+    /* check protocol */
+    if(!(LuaStateNeedProto(luastate, ALPROTO_SMTP))) {
+        return LuaCallbackError(luastate, "Error: protocol not SMTP");
+    }
+    /* create lockhint var for flowlock check. */
+    int lock_hint = 0;
+    /* Extract flow, with lockhint to check mutexlocking */
+    Flow *flow = LuaStateGetFlow(luastate, &lock_hint);
+    if(flow == NULL) {
+        return LuaCallbackError(luastate, "Internal error: no flow");
+    }
+    /* check if already mutexlocked by parents */
+    if(lock_hint == LUA_FLOW_NOT_LOCKED_BY_PARENT) {
+        /* lock flow */
+        FLOWLOCK_RDLOCK(flow);
+        GetRcptList(luastate, flow);
+        /* open flow */
+        FLOWLOCK_UNLOCK(flow);
+    } else {
+        GetRcptList(luastate, flow);
+    }
+    /* return 1 since we allways push one table to luastate */
+    return 1;
+}
+
+int LuaRegisterSmtpFunctions(lua_State *luastate)
+{
+
+    lua_pushcfunction(luastate, SMTPGetMimeField);
+    lua_setglobal(luastate, "SMTPGetMimeField");
+
+    lua_pushcfunction(luastate, SMTPGetMimeList);
+    lua_setglobal(luastate, "SMTPGetMimeList");
+
+    lua_pushcfunction(luastate, SMTPGetMailFrom);
+    lua_setglobal(luastate, "SMTPGetMailFrom");
+
+    lua_pushcfunction(luastate, SMTPGetRcptList);
+    lua_setglobal(luastate, "SMTPGetRcptList");
+
+    return 0;
+}
+
+#endif /* HAVE_LUA */
diff --git a/src/util-lua-smtp.h b/src/util-lua-smtp.h
new file mode 100644 (file)
index 0000000..26750b8
--- /dev/null
@@ -0,0 +1,27 @@
+/* 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.
+ */
+
+#ifndef __UTIL_LUA_SMTP_H__
+#define __UTIL_LUA_SMTP_H__
+
+#ifdef HAVE_LUA
+
+int LuaRegisterSmtpFunctions(lua_State *luastate);
+
+#endif /* HAVE_LUA */
+
+#endif /* __UTIL_LUA_SMTP_H__ */