diff options
Diffstat (limited to '')
-rw-r--r-- | src/detect-lua.c | 2559 |
1 files changed, 2559 insertions, 0 deletions
diff --git a/src/detect-lua.c b/src/detect-lua.c new file mode 100644 index 0000000..dfb26dc --- /dev/null +++ b/src/detect-lua.c @@ -0,0 +1,2559 @@ +/* Copyright (C) 2007-2022 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> + * + */ + +#include "suricata-common.h" +#include "conf.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-build.h" + +#include "detect-byte.h" + +#include "flow.h" +#include "flow-var.h" +#include "flow-util.h" + +#include "util-debug.h" +#include "util-spm-bm.h" +#include "util-print.h" +#include "util-byte.h" + +#include "util-unittest.h" +#include "util-unittest-helper.h" + +#include "app-layer.h" +#include "app-layer-parser.h" +#include "app-layer-htp.h" + +#include "stream-tcp.h" + +#include "detect-lua.h" +#include "detect-lua-extensions.h" + +#include "queue.h" +#include "util-cpu.h" +#include "util-var-name.h" + +#ifndef HAVE_LUA + +static int DetectLuaSetupNoSupport (DetectEngineCtx *a, Signature *b, const char *c) +{ + SCLogError("no Lua support built in, needed for lua/luajit keyword"); + return -1; +} + +/** + * \brief Registration function for keyword: lua + */ +void DetectLuaRegister(void) +{ + sigmatch_table[DETECT_LUA].name = "lua"; + sigmatch_table[DETECT_LUA].alias = "luajit"; + sigmatch_table[DETECT_LUA].desc = "support for lua scripting"; + sigmatch_table[DETECT_LUA].url = "/rules/rule-lua-scripting.html"; + sigmatch_table[DETECT_LUA].Setup = DetectLuaSetupNoSupport; + sigmatch_table[DETECT_LUA].Free = NULL; + sigmatch_table[DETECT_LUA].flags = SIGMATCH_NOT_BUILT; + + SCLogDebug("registering lua rule option"); + return; +} + +#else /* HAVE_LUA */ + +#include "util-lua.h" + +static int DetectLuaMatch (DetectEngineThreadCtx *, + Packet *, const Signature *, const SigMatchCtx *); +static int DetectLuaAppTxMatch (DetectEngineThreadCtx *det_ctx, + Flow *f, uint8_t flags, + void *state, void *txv, const Signature *s, + const SigMatchCtx *ctx); +static int DetectLuaSetup (DetectEngineCtx *, Signature *, const char *); +#ifdef UNITTESTS +static void DetectLuaRegisterTests(void); +#endif +static void DetectLuaFree(DetectEngineCtx *, void *); +static int g_smtp_generic_list_id = 0; + +/** + * \brief Registration function for keyword: lua + */ +void DetectLuaRegister(void) +{ + sigmatch_table[DETECT_LUA].name = "lua"; + sigmatch_table[DETECT_LUA].alias = "luajit"; + sigmatch_table[DETECT_LUA].desc = "match via a lua script"; + sigmatch_table[DETECT_LUA].url = "/rules/rule-lua-scripting.html"; + sigmatch_table[DETECT_LUA].Match = DetectLuaMatch; + sigmatch_table[DETECT_LUA].AppLayerTxMatch = DetectLuaAppTxMatch; + sigmatch_table[DETECT_LUA].Setup = DetectLuaSetup; + sigmatch_table[DETECT_LUA].Free = DetectLuaFree; +#ifdef UNITTESTS + sigmatch_table[DETECT_LUA].RegisterTests = DetectLuaRegisterTests; +#endif + g_smtp_generic_list_id = DetectBufferTypeRegister("smtp_generic"); + + DetectAppLayerInspectEngineRegister2("smtp_generic", ALPROTO_SMTP, SIG_FLAG_TOSERVER, 0, + DetectEngineInspectGenericList, NULL); + DetectAppLayerInspectEngineRegister2("smtp_generic", ALPROTO_SMTP, SIG_FLAG_TOCLIENT, 0, + DetectEngineInspectGenericList, NULL); + + SCLogDebug("registering lua rule option"); + return; +} + +#define DATATYPE_PACKET BIT_U32(0) +#define DATATYPE_PAYLOAD BIT_U32(1) +#define DATATYPE_STREAM BIT_U32(2) + +#define DATATYPE_HTTP_URI BIT_U32(3) +#define DATATYPE_HTTP_URI_RAW BIT_U32(4) + +#define DATATYPE_HTTP_REQUEST_HEADERS BIT_U32(5) +#define DATATYPE_HTTP_REQUEST_HEADERS_RAW BIT_U32(6) +#define DATATYPE_HTTP_REQUEST_COOKIE BIT_U32(7) +#define DATATYPE_HTTP_REQUEST_UA BIT_U32(8) + +#define DATATYPE_HTTP_REQUEST_LINE BIT_U32(9) +#define DATATYPE_HTTP_REQUEST_BODY BIT_U32(10) + +#define DATATYPE_HTTP_RESPONSE_COOKIE BIT_U32(11) +#define DATATYPE_HTTP_RESPONSE_BODY BIT_U32(12) + +#define DATATYPE_HTTP_RESPONSE_HEADERS BIT_U32(13) +#define DATATYPE_HTTP_RESPONSE_HEADERS_RAW BIT_U32(14) + +#define DATATYPE_DNS_RRNAME BIT_U32(15) +#define DATATYPE_DNS_REQUEST BIT_U32(16) +#define DATATYPE_DNS_RESPONSE BIT_U32(17) + +#define DATATYPE_TLS BIT_U32(18) +#define DATATYPE_SSH BIT_U32(19) +#define DATATYPE_SMTP BIT_U32(20) + +#define DATATYPE_DNP3 BIT_U32(21) + +#define DATATYPE_BUFFER BIT_U32(22) + +#if 0 +/** \brief dump stack from lua state to screen */ +void LuaDumpStack(lua_State *state) +{ + int size = lua_gettop(state); + int i; + + for (i = 1; i <= size; i++) { + int type = lua_type(state, i); + printf("Stack size=%d, level=%d, type=%d, ", size, i, type); + + switch (type) { + case LUA_TFUNCTION: + printf("function %s", lua_tostring(state, i) ? "true" : "false"); + break; + case LUA_TBOOLEAN: + printf("bool %s", lua_toboolean(state, i) ? "true" : "false"); + break; + case LUA_TNUMBER: + printf("number %g", lua_tonumber(state, i)); + break; + case LUA_TSTRING: + printf("string `%s'", lua_tostring(state, i)); + break; + case LUA_TTABLE: + printf("table `%s'", lua_tostring(state, i)); + break; + default: + printf("other %s", lua_typename(state, type)); + break; + + } + printf("\n"); + } +} +#endif + +int DetectLuaMatchBuffer(DetectEngineThreadCtx *det_ctx, + const Signature *s, const SigMatchData *smd, + const uint8_t *buffer, uint32_t buffer_len, uint32_t offset, + Flow *f) +{ + SCEnter(); + int ret = 0; + + if (buffer == NULL || buffer_len == 0) + SCReturnInt(0); + + DetectLuaData *lua = (DetectLuaData *)smd->ctx; + if (lua == NULL) + SCReturnInt(0); + + DetectLuaThreadData *tlua = (DetectLuaThreadData *)DetectThreadCtxGetKeywordThreadCtx(det_ctx, lua->thread_ctx_id); + if (tlua == NULL) + SCReturnInt(0); + + LuaExtensionsMatchSetup(tlua->luastate, lua, det_ctx, f, /* no packet in the ctx */ NULL, s, 0); + + /* prepare data to pass to script */ + lua_getglobal(tlua->luastate, "match"); + lua_newtable(tlua->luastate); /* stack at -1 */ + + lua_pushliteral (tlua->luastate, "offset"); /* stack at -2 */ + lua_pushnumber (tlua->luastate, (int)(offset + 1)); + lua_settable(tlua->luastate, -3); + + lua_pushstring (tlua->luastate, lua->buffername); /* stack at -2 */ + LuaPushStringBuffer(tlua->luastate, (const uint8_t *)buffer, (size_t)buffer_len); + lua_settable(tlua->luastate, -3); + + int retval = lua_pcall(tlua->luastate, 1, 1, 0); + if (retval != 0) { + SCLogInfo("failed to run script: %s", lua_tostring(tlua->luastate, -1)); + } + + /* process returns from script */ + if (lua_gettop(tlua->luastate) > 0) { + /* script returns a number (return 1 or return 0) */ + if (lua_type(tlua->luastate, 1) == LUA_TNUMBER) { + double script_ret = lua_tonumber(tlua->luastate, 1); + SCLogDebug("script_ret %f", script_ret); + lua_pop(tlua->luastate, 1); + + if (script_ret == 1.0) + ret = 1; + + /* script returns a table */ + } else if (lua_type(tlua->luastate, 1) == LUA_TTABLE) { + lua_pushnil(tlua->luastate); + const char *k, *v; + while (lua_next(tlua->luastate, -2)) { + v = lua_tostring(tlua->luastate, -1); + lua_pop(tlua->luastate, 1); + k = lua_tostring(tlua->luastate, -1); + + if (!k || !v) + continue; + + SCLogDebug("k='%s', v='%s'", k, v); + + if (strcmp(k, "retval") == 0) { + int val; + if (StringParseInt32(&val, 10, 0, (const char *)v) < 0) { + SCLogError("Invalid value " + "for \"retval\" from LUA return table: '%s'", + v); + ret = 0; + } + else if (val == 1) { + ret = 1; + } + } else { + /* set flow var? */ + } + } + + /* pop the table */ + lua_pop(tlua->luastate, 1); + } + } else { + SCLogDebug("no stack"); + } + + /* clear the stack */ + while (lua_gettop(tlua->luastate) > 0) { + lua_pop(tlua->luastate, 1); + } + + if (lua->negated) { + if (ret == 1) + ret = 0; + else + ret = 1; + } + + SCReturnInt(ret); +} + +/** + * \brief match the specified lua script + * + * \param t thread local vars + * \param det_ctx pattern matcher thread local data + * \param p packet + * \param s signature being inspected + * \param m sigmatch that we will cast into DetectLuaData + * + * \retval 0 no match + * \retval 1 match + */ +static int DetectLuaMatch (DetectEngineThreadCtx *det_ctx, + Packet *p, const Signature *s, const SigMatchCtx *ctx) +{ + SCEnter(); + int ret = 0; + DetectLuaData *lua = (DetectLuaData *)ctx; + if (lua == NULL) + SCReturnInt(0); + + DetectLuaThreadData *tlua = (DetectLuaThreadData *)DetectThreadCtxGetKeywordThreadCtx(det_ctx, lua->thread_ctx_id); + if (tlua == NULL) + SCReturnInt(0); + + /* setup extension data for use in lua c functions */ + uint8_t flags = 0; + if (p->flowflags & FLOW_PKT_TOSERVER) + flags = STREAM_TOSERVER; + else if (p->flowflags & FLOW_PKT_TOCLIENT) + flags = STREAM_TOCLIENT; + + LuaStateSetThreadVars(tlua->luastate, det_ctx->tv); + + LuaExtensionsMatchSetup(tlua->luastate, lua, det_ctx, p->flow, p, s, flags); + + if ((tlua->flags & DATATYPE_PAYLOAD) && p->payload_len == 0) + SCReturnInt(0); + if ((tlua->flags & DATATYPE_PACKET) && GET_PKT_LEN(p) == 0) + SCReturnInt(0); + if (tlua->alproto != ALPROTO_UNKNOWN) { + if (p->flow == NULL) + SCReturnInt(0); + + AppProto alproto = p->flow->alproto; + if (tlua->alproto != alproto) + SCReturnInt(0); + } + + lua_getglobal(tlua->luastate, "match"); + lua_newtable(tlua->luastate); /* stack at -1 */ + + if ((tlua->flags & DATATYPE_PAYLOAD) && p->payload_len) { + lua_pushliteral(tlua->luastate, "payload"); /* stack at -2 */ + LuaPushStringBuffer (tlua->luastate, (const uint8_t *)p->payload, (size_t)p->payload_len); /* stack at -3 */ + lua_settable(tlua->luastate, -3); + } + if ((tlua->flags & DATATYPE_PACKET) && GET_PKT_LEN(p)) { + lua_pushliteral(tlua->luastate, "packet"); /* stack at -2 */ + LuaPushStringBuffer (tlua->luastate, (const uint8_t *)GET_PKT_DATA(p), (size_t)GET_PKT_LEN(p)); /* stack at -3 */ + lua_settable(tlua->luastate, -3); + } + if (tlua->alproto == ALPROTO_HTTP1) { + HtpState *htp_state = p->flow->alstate; + if (htp_state != NULL && htp_state->connp != NULL) { + htp_tx_t *tx = NULL; + uint64_t idx = AppLayerParserGetTransactionInspectId(p->flow->alparser, + STREAM_TOSERVER); + uint64_t total_txs= AppLayerParserGetTxCnt(p->flow, htp_state); + for ( ; idx < total_txs; idx++) { + tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, htp_state, idx); + if (tx == NULL) + continue; + + if ((tlua->flags & DATATYPE_HTTP_REQUEST_LINE) && tx->request_line != NULL && + bstr_len(tx->request_line) > 0) { + lua_pushliteral(tlua->luastate, "http.request_line"); /* stack at -2 */ + LuaPushStringBuffer(tlua->luastate, + (const uint8_t *)bstr_ptr(tx->request_line), + bstr_len(tx->request_line)); + lua_settable(tlua->luastate, -3); + } + } + } + } + + int retval = lua_pcall(tlua->luastate, 1, 1, 0); + if (retval != 0) { + SCLogInfo("failed to run script: %s", lua_tostring(tlua->luastate, -1)); + } + + /* process returns from script */ + if (lua_gettop(tlua->luastate) > 0) { + + /* script returns a number (return 1 or return 0) */ + if (lua_type(tlua->luastate, 1) == LUA_TNUMBER) { + double script_ret = lua_tonumber(tlua->luastate, 1); + SCLogDebug("script_ret %f", script_ret); + lua_pop(tlua->luastate, 1); + + if (script_ret == 1.0) + ret = 1; + + /* script returns a table */ + } else if (lua_type(tlua->luastate, 1) == LUA_TTABLE) { + lua_pushnil(tlua->luastate); + const char *k, *v; + while (lua_next(tlua->luastate, -2)) { + v = lua_tostring(tlua->luastate, -1); + lua_pop(tlua->luastate, 1); + k = lua_tostring(tlua->luastate, -1); + + if (!k || !v) + continue; + + SCLogDebug("k='%s', v='%s'", k, v); + + if (strcmp(k, "retval") == 0) { + int val; + if (StringParseInt32(&val, 10, 0, + (const char *)v) < 0) { + SCLogError("Invalid value " + "for \"retval\" from LUA return table: '%s'", + v); + ret = 0; + } + else if (val == 1) { + ret = 1; + } + } else { + /* set flow var? */ + } + } + + /* pop the table */ + lua_pop(tlua->luastate, 1); + } + } + while (lua_gettop(tlua->luastate) > 0) { + lua_pop(tlua->luastate, 1); + } + + if (lua->negated) { + if (ret == 1) + ret = 0; + else + ret = 1; + } + + SCReturnInt(ret); +} + +static int DetectLuaAppMatchCommon (DetectEngineThreadCtx *det_ctx, + Flow *f, uint8_t flags, void *state, + const Signature *s, const SigMatchCtx *ctx) +{ + SCEnter(); + int ret = 0; + DetectLuaData *lua = (DetectLuaData *)ctx; + if (lua == NULL) + SCReturnInt(0); + + DetectLuaThreadData *tlua = (DetectLuaThreadData *)DetectThreadCtxGetKeywordThreadCtx(det_ctx, lua->thread_ctx_id); + if (tlua == NULL) + SCReturnInt(0); + + /* setup extension data for use in lua c functions */ + LuaExtensionsMatchSetup(tlua->luastate, lua, det_ctx, f, NULL, s, flags); + + if (tlua->alproto != ALPROTO_UNKNOWN) { + int alproto = f->alproto; + if (tlua->alproto != alproto) + SCReturnInt(0); + } + + lua_getglobal(tlua->luastate, "match"); + lua_newtable(tlua->luastate); /* stack at -1 */ + + if (tlua->alproto == ALPROTO_HTTP1) { + HtpState *htp_state = state; + if (htp_state != NULL && htp_state->connp != NULL) { + htp_tx_t *tx = NULL; + tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, htp_state, det_ctx->tx_id); + if (tx != NULL) { + if ((tlua->flags & DATATYPE_HTTP_REQUEST_LINE) && tx->request_line != NULL && + bstr_len(tx->request_line) > 0) { + lua_pushliteral(tlua->luastate, "http.request_line"); /* stack at -2 */ + LuaPushStringBuffer(tlua->luastate, + (const uint8_t *)bstr_ptr(tx->request_line), + bstr_len(tx->request_line)); + lua_settable(tlua->luastate, -3); + } + } + } + } + + int retval = lua_pcall(tlua->luastate, 1, 1, 0); + if (retval != 0) { + SCLogInfo("failed to run script: %s", lua_tostring(tlua->luastate, -1)); + } + + /* process returns from script */ + if (lua_gettop(tlua->luastate) > 0) { + + /* script returns a number (return 1 or return 0) */ + if (lua_type(tlua->luastate, 1) == LUA_TNUMBER) { + double script_ret = lua_tonumber(tlua->luastate, 1); + SCLogDebug("script_ret %f", script_ret); + lua_pop(tlua->luastate, 1); + + if (script_ret == 1.0) + ret = 1; + + /* script returns a table */ + } else if (lua_type(tlua->luastate, 1) == LUA_TTABLE) { + lua_pushnil(tlua->luastate); + const char *k, *v; + while (lua_next(tlua->luastate, -2)) { + v = lua_tostring(tlua->luastate, -1); + lua_pop(tlua->luastate, 1); + k = lua_tostring(tlua->luastate, -1); + + if (!k || !v) + continue; + + SCLogDebug("k='%s', v='%s'", k, v); + + if (strcmp(k, "retval") == 0) { + int val; + if (StringParseInt32(&val, 10, 0, + (const char *)v) < 0) { + SCLogError("Invalid value " + "for \"retval\" from LUA return table: '%s'", + v); + ret = 0; + } + else if (val == 1) { + ret = 1; + } + } else { + /* set flow var? */ + } + } + + /* pop the table */ + lua_pop(tlua->luastate, 1); + } + } + while (lua_gettop(tlua->luastate) > 0) { + lua_pop(tlua->luastate, 1); + } + + if (lua->negated) { + if (ret == 1) + ret = 0; + else + ret = 1; + } + + SCReturnInt(ret); +} + +/** + * \brief match the specified lua script in a list with a tx + * + * \param t thread local vars + * \param det_ctx pattern matcher thread local data + * \param s signature being inspected + * \param m sigmatch that we will cast into DetectLuaData + * + * \retval 0 no match + * \retval 1 match + */ +static int DetectLuaAppTxMatch (DetectEngineThreadCtx *det_ctx, + Flow *f, uint8_t flags, + void *state, void *txv, const Signature *s, + const SigMatchCtx *ctx) +{ + return DetectLuaAppMatchCommon(det_ctx, f, flags, state, s, ctx); +} + +#ifdef UNITTESTS +/* if this ptr is set the lua setup functions will use this buffer as the + * lua script instead of calling luaL_loadfile on the filename supplied. */ +static const char *ut_script = NULL; +#endif + +static void *DetectLuaThreadInit(void *data) +{ + int status; + DetectLuaData *lua = (DetectLuaData *)data; + BUG_ON(lua == NULL); + + DetectLuaThreadData *t = SCMalloc(sizeof(DetectLuaThreadData)); + if (unlikely(t == NULL)) { + SCLogError("couldn't alloc ctx memory"); + return NULL; + } + memset(t, 0x00, sizeof(DetectLuaThreadData)); + + t->alproto = lua->alproto; + t->flags = lua->flags; + + t->luastate = LuaGetState(); + if (t->luastate == NULL) { + SCLogError("luastate pool depleted"); + goto error; + } + + luaL_openlibs(t->luastate); + + LuaRegisterExtensions(t->luastate); + + lua_pushinteger(t->luastate, (lua_Integer)(lua->sid)); + lua_setglobal(t->luastate, "SCRuleSid"); + lua_pushinteger(t->luastate, (lua_Integer)(lua->rev)); + lua_setglobal(t->luastate, "SCRuleRev"); + lua_pushinteger(t->luastate, (lua_Integer)(lua->gid)); + lua_setglobal(t->luastate, "SCRuleGid"); + + /* hackish, needed to allow unittests to pass buffers as scripts instead of files */ +#ifdef UNITTESTS + if (ut_script != NULL) { + status = luaL_loadbuffer(t->luastate, ut_script, strlen(ut_script), "unittest"); + if (status) { + SCLogError("couldn't load file: %s", lua_tostring(t->luastate, -1)); + goto error; + } + } else { +#endif + status = luaL_loadfile(t->luastate, lua->filename); + if (status) { + SCLogError("couldn't load file: %s", lua_tostring(t->luastate, -1)); + goto error; + } +#ifdef UNITTESTS + } +#endif + + /* prime the script (or something) */ + if (lua_pcall(t->luastate, 0, 0, 0) != 0) { + SCLogError("couldn't prime file: %s", lua_tostring(t->luastate, -1)); + goto error; + } + + return (void *)t; + +error: + if (t->luastate != NULL) + LuaReturnState(t->luastate); + SCFree(t); + return NULL; +} + +static void DetectLuaThreadFree(void *ctx) +{ + if (ctx != NULL) { + DetectLuaThreadData *t = (DetectLuaThreadData *)ctx; + if (t->luastate != NULL) + LuaReturnState(t->luastate); + SCFree(t); + } +} + +/** + * \brief Parse the lua keyword + * + * \param de_ctx Pointer to the detection engine context + * \param str Pointer to the user provided option + * + * \retval lua pointer to DetectLuaData on success + * \retval NULL on failure + */ +static DetectLuaData *DetectLuaParse (DetectEngineCtx *de_ctx, const char *str) +{ + DetectLuaData *lua = NULL; + + /* We have a correct lua option */ + lua = SCMalloc(sizeof(DetectLuaData)); + if (unlikely(lua == NULL)) + goto error; + + memset(lua, 0x00, sizeof(DetectLuaData)); + + if (strlen(str) && str[0] == '!') { + lua->negated = 1; + str++; + } + + /* get full filename */ + lua->filename = DetectLoadCompleteSigPath(de_ctx, str); + if (lua->filename == NULL) { + goto error; + } + + return lua; + +error: + if (lua != NULL) + DetectLuaFree(de_ctx, lua); + return NULL; +} + +static int DetectLuaSetupPrime(DetectEngineCtx *de_ctx, DetectLuaData *ld, const Signature *s) +{ + int status; + + lua_State *luastate = luaL_newstate(); + if (luastate == NULL) + return -1; + luaL_openlibs(luastate); + + /* hackish, needed to allow unittests to pass buffers as scripts instead of files */ +#ifdef UNITTESTS + if (ut_script != NULL) { + status = luaL_loadbuffer(luastate, ut_script, strlen(ut_script), "unittest"); + if (status) { + SCLogError("couldn't load file: %s", lua_tostring(luastate, -1)); + goto error; + } + } else { +#endif + status = luaL_loadfile(luastate, ld->filename); + if (status) { + SCLogError("couldn't load file: %s", lua_tostring(luastate, -1)); + goto error; + } +#ifdef UNITTESTS + } +#endif + + /* prime the script (or something) */ + if (lua_pcall(luastate, 0, 0, 0) != 0) { + SCLogError("couldn't prime file: %s", lua_tostring(luastate, -1)); + goto error; + } + + lua_getglobal(luastate, "init"); + if (lua_type(luastate, -1) != LUA_TFUNCTION) { + SCLogError("no init function in script"); + goto error; + } + + lua_newtable(luastate); /* stack at -1 */ + if (lua_gettop(luastate) == 0 || lua_type(luastate, 2) != LUA_TTABLE) { + SCLogError("no table setup"); + goto error; + } + + lua_pushliteral(luastate, "script_api_ver"); /* stack at -2 */ + lua_pushnumber (luastate, 1); /* stack at -3 */ + lua_settable(luastate, -3); + + if (lua_pcall(luastate, 1, 1, 0) != 0) { + SCLogError("couldn't run script 'init' function: %s", lua_tostring(luastate, -1)); + goto error; + } + + /* process returns from script */ + if (lua_gettop(luastate) == 0) { + SCLogError("init function in script should return table, nothing returned"); + goto error; + } + if (lua_type(luastate, 1) != LUA_TTABLE) { + SCLogError("init function in script should return table, returned is not table"); + goto error; + } + + lua_pushnil(luastate); + const char *k, *v; + while (lua_next(luastate, -2)) { + k = lua_tostring(luastate, -2); + if (k == NULL) + continue; + + /* handle flowvar and bytes separately as they have a table as value */ + if (strcmp(k, "flowvar") == 0) { + if (lua_istable(luastate, -1)) { + lua_pushnil(luastate); + while (lua_next(luastate, -2) != 0) { + /* value at -1, key is at -2 which we ignore */ + const char *value = lua_tostring(luastate, -1); + SCLogDebug("value %s", value); + /* removes 'value'; keeps 'key' for next iteration */ + lua_pop(luastate, 1); + + if (ld->flowvars == DETECT_LUAJIT_MAX_FLOWVARS) { + SCLogError("too many flowvars registered"); + goto error; + } + + uint32_t idx = VarNameStoreRegister(value, VAR_TYPE_FLOW_VAR); + ld->flowvar[ld->flowvars++] = idx; + SCLogDebug("script uses flowvar %u with script id %u", idx, ld->flowvars - 1); + } + } + lua_pop(luastate, 1); + continue; + } else if (strcmp(k, "flowint") == 0) { + if (lua_istable(luastate, -1)) { + lua_pushnil(luastate); + while (lua_next(luastate, -2) != 0) { + /* value at -1, key is at -2 which we ignore */ + const char *value = lua_tostring(luastate, -1); + SCLogDebug("value %s", value); + /* removes 'value'; keeps 'key' for next iteration */ + lua_pop(luastate, 1); + + if (ld->flowints == DETECT_LUAJIT_MAX_FLOWINTS) { + SCLogError("too many flowints registered"); + goto error; + } + + uint32_t idx = VarNameStoreRegister(value, VAR_TYPE_FLOW_INT); + ld->flowint[ld->flowints++] = idx; + SCLogDebug("script uses flowint %u with script id %u", idx, ld->flowints - 1); + } + } + lua_pop(luastate, 1); + continue; + } else if (strcmp(k, "bytevar") == 0) { + if (lua_istable(luastate, -1)) { + lua_pushnil(luastate); + while (lua_next(luastate, -2) != 0) { + /* value at -1, key is at -2 which we ignore */ + const char *value = lua_tostring(luastate, -1); + SCLogDebug("value %s", value); + /* removes 'value'; keeps 'key' for next iteration */ + lua_pop(luastate, 1); + + if (ld->bytevars == DETECT_LUAJIT_MAX_BYTEVARS) { + SCLogError("too many bytevars registered"); + goto error; + } + + DetectByteIndexType idx; + if (!DetectByteRetrieveSMVar(value, s, &idx)) { + SCLogError("Unknown byte_extract or byte_math var " + "requested by lua script - %s", + value); + goto error; + } + ld->bytevar[ld->bytevars++] = idx; + SCLogDebug("script uses bytevar %u with script id %u", idx, ld->bytevars - 1); + } + } + lua_pop(luastate, 1); + continue; + } + + v = lua_tostring(luastate, -1); + lua_pop(luastate, 1); + if (v == NULL) + continue; + + SCLogDebug("k='%s', v='%s'", k, v); + if (strcmp(k, "packet") == 0 && strcmp(v, "true") == 0) { + ld->flags |= DATATYPE_PACKET; + } else if (strcmp(k, "payload") == 0 && strcmp(v, "true") == 0) { + ld->flags |= DATATYPE_PAYLOAD; + } else if (strcmp(k, "buffer") == 0 && strcmp(v, "true") == 0) { + ld->flags |= DATATYPE_BUFFER; + + ld->buffername = SCStrdup("buffer"); + if (ld->buffername == NULL) { + SCLogError("alloc error"); + goto error; + } + } else if (strcmp(k, "stream") == 0 && strcmp(v, "true") == 0) { + ld->flags |= DATATYPE_STREAM; + + ld->buffername = SCStrdup("stream"); + if (ld->buffername == NULL) { + SCLogError("alloc error"); + goto error; + } + + } else if (strncmp(k, "http", 4) == 0 && strcmp(v, "true") == 0) { + if (ld->alproto != ALPROTO_UNKNOWN && ld->alproto != ALPROTO_HTTP1) { + SCLogError( + "can just inspect script against one app layer proto like HTTP at a time"); + goto error; + } + if (ld->flags != 0) { + SCLogError("when inspecting HTTP buffers only a single buffer can be inspected"); + goto error; + } + + /* http types */ + ld->alproto = ALPROTO_HTTP1; + + if (strcmp(k, "http.uri") == 0) + ld->flags |= DATATYPE_HTTP_URI; + + else if (strcmp(k, "http.uri.raw") == 0) + ld->flags |= DATATYPE_HTTP_URI_RAW; + + else if (strcmp(k, "http.request_line") == 0) + ld->flags |= DATATYPE_HTTP_REQUEST_LINE; + + else if (strcmp(k, "http.request_headers") == 0) + ld->flags |= DATATYPE_HTTP_REQUEST_HEADERS; + + else if (strcmp(k, "http.request_headers.raw") == 0) + ld->flags |= DATATYPE_HTTP_REQUEST_HEADERS_RAW; + + else if (strcmp(k, "http.request_cookie") == 0) + ld->flags |= DATATYPE_HTTP_REQUEST_COOKIE; + + else if (strcmp(k, "http.request_user_agent") == 0) + ld->flags |= DATATYPE_HTTP_REQUEST_UA; + + else if (strcmp(k, "http.request_body") == 0) + ld->flags |= DATATYPE_HTTP_REQUEST_BODY; + + else if (strcmp(k, "http.response_body") == 0) + ld->flags |= DATATYPE_HTTP_RESPONSE_BODY; + + else if (strcmp(k, "http.response_cookie") == 0) + ld->flags |= DATATYPE_HTTP_RESPONSE_COOKIE; + + else if (strcmp(k, "http.response_headers") == 0) + ld->flags |= DATATYPE_HTTP_RESPONSE_HEADERS; + + else if (strcmp(k, "http.response_headers.raw") == 0) + ld->flags |= DATATYPE_HTTP_RESPONSE_HEADERS_RAW; + + else { + SCLogError("unsupported http data type %s", k); + goto error; + } + + ld->buffername = SCStrdup(k); + if (ld->buffername == NULL) { + SCLogError("alloc error"); + goto error; + } + } else if (strncmp(k, "dns", 3) == 0 && strcmp(v, "true") == 0) { + + ld->alproto = ALPROTO_DNS; + + if (strcmp(k, "dns.rrname") == 0) + ld->flags |= DATATYPE_DNS_RRNAME; + else if (strcmp(k, "dns.request") == 0) + ld->flags |= DATATYPE_DNS_REQUEST; + else if (strcmp(k, "dns.response") == 0) + ld->flags |= DATATYPE_DNS_RESPONSE; + + else { + SCLogError("unsupported dns data type %s", k); + goto error; + } + ld->buffername = SCStrdup(k); + if (ld->buffername == NULL) { + SCLogError("alloc error"); + goto error; + } + } else if (strncmp(k, "tls", 3) == 0 && strcmp(v, "true") == 0) { + + ld->alproto = ALPROTO_TLS; + + ld->flags |= DATATYPE_TLS; + + } else if (strncmp(k, "ssh", 3) == 0 && strcmp(v, "true") == 0) { + + ld->alproto = ALPROTO_SSH; + + ld->flags |= DATATYPE_SSH; + + } else if (strncmp(k, "smtp", 4) == 0 && strcmp(v, "true") == 0) { + + ld->alproto = ALPROTO_SMTP; + + ld->flags |= DATATYPE_SMTP; + + } else if (strncmp(k, "dnp3", 4) == 0 && strcmp(v, "true") == 0) { + + ld->alproto = ALPROTO_DNP3; + + ld->flags |= DATATYPE_DNP3; + + } else { + SCLogError("unsupported data type %s", k); + goto error; + } + } + + /* pop the table */ + lua_pop(luastate, 1); + lua_close(luastate); + return 0; +error: + lua_close(luastate); + return -1; +} + +/** + * \brief this function is used to parse lua options + * \brief into the current signature + * + * \param de_ctx pointer to the Detection Engine Context + * \param s pointer to the Current Signature + * \param str pointer to the user provided "lua" option + * + * \retval 0 on Success + * \retval -1 on Failure + */ +static int DetectLuaSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str) +{ + DetectLuaData *lua = NULL; + SigMatch *sm = NULL; + + /* First check if Lua rules are enabled, by default Lua in rules + * is disabled. */ + int enabled = 0; + (void)ConfGetBool("security.lua.allow-rules", &enabled); + if (!enabled) { + SCLogError("Lua rules disabled by security configuration: security.lua.allow-rules"); + goto error; + } + + lua = DetectLuaParse(de_ctx, str); + if (lua == NULL) + goto error; + + if (DetectLuaSetupPrime(de_ctx, lua, s) == -1) { + goto error; + } + + lua->thread_ctx_id = DetectRegisterThreadCtxFuncs(de_ctx, "lua", + DetectLuaThreadInit, (void *)lua, + DetectLuaThreadFree, 0); + if (lua->thread_ctx_id == -1) + goto error; + + if (lua->alproto != ALPROTO_UNKNOWN) { + if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, lua->alproto)) { + goto error; + } + s->alproto = lua->alproto; + } + + /* Okay so far so good, lets get this into a SigMatch + * and put it in the Signature. */ + sm = SigMatchAlloc(); + if (sm == NULL) + goto error; + + sm->type = DETECT_LUA; + sm->ctx = (SigMatchCtx *)lua; + + int list = -1; + if (lua->alproto == ALPROTO_UNKNOWN) { + if (lua->flags & DATATYPE_STREAM) + list = DETECT_SM_LIST_PMATCH; + else { + if (lua->flags & DATATYPE_BUFFER) { + if (DetectBufferGetActiveList(de_ctx, s) != -1) { + list = s->init_data->list; + } else { + SCLogError("Lua and sticky buffer failure"); + goto error; + } + } else + list = DETECT_SM_LIST_MATCH; + } + + } else if (lua->alproto == ALPROTO_HTTP1) { + if (lua->flags & DATATYPE_HTTP_RESPONSE_BODY) { + list = DetectBufferTypeGetByName("file_data"); + } else if (lua->flags & DATATYPE_HTTP_REQUEST_BODY) { + list = DetectBufferTypeGetByName("http_client_body"); + } else if (lua->flags & DATATYPE_HTTP_URI) { + list = DetectBufferTypeGetByName("http_uri"); + } else if (lua->flags & DATATYPE_HTTP_URI_RAW) { + list = DetectBufferTypeGetByName("http_raw_uri"); + } else if (lua->flags & DATATYPE_HTTP_REQUEST_COOKIE || + lua->flags & DATATYPE_HTTP_RESPONSE_COOKIE) + { + list = DetectBufferTypeGetByName("http_cookie"); + } else if (lua->flags & DATATYPE_HTTP_REQUEST_UA) { + list = DetectBufferTypeGetByName("http_user_agent"); + } else if (lua->flags & (DATATYPE_HTTP_REQUEST_HEADERS|DATATYPE_HTTP_RESPONSE_HEADERS)) { + list = DetectBufferTypeGetByName("http_header"); + } else if (lua->flags & (DATATYPE_HTTP_REQUEST_HEADERS_RAW|DATATYPE_HTTP_RESPONSE_HEADERS_RAW)) { + list = DetectBufferTypeGetByName("http_raw_header"); + } else { + list = DetectBufferTypeGetByName("http_request_line"); + } + } else if (lua->alproto == ALPROTO_DNS) { + if (lua->flags & DATATYPE_DNS_RRNAME) { + list = DetectBufferTypeGetByName("dns_query"); + } else if (lua->flags & DATATYPE_DNS_REQUEST) { + list = DetectBufferTypeGetByName("dns_request"); + } else if (lua->flags & DATATYPE_DNS_RESPONSE) { + list = DetectBufferTypeGetByName("dns_response"); + } + } else if (lua->alproto == ALPROTO_TLS) { + list = DetectBufferTypeGetByName("tls_generic"); + } else if (lua->alproto == ALPROTO_SSH) { + list = DetectBufferTypeGetByName("ssh_banner"); + } else if (lua->alproto == ALPROTO_SMTP) { + list = g_smtp_generic_list_id; + } else if (lua->alproto == ALPROTO_DNP3) { + list = DetectBufferTypeGetByName("dnp3"); + } else { + SCLogError("lua can't be used with protocol %s", AppLayerGetProtoName(lua->alproto)); + goto error; + } + + if (list == -1) { + SCLogError("lua can't be used with protocol %s", AppLayerGetProtoName(lua->alproto)); + goto error; + } + + SigMatchAppendSMToList(s, sm, list); + + return 0; + +error: + if (lua != NULL) + DetectLuaFree(de_ctx, lua); + if (sm != NULL) + SCFree(sm); + return -1; +} + +/** \brief post-sig parse function to set the sid,rev,gid into the + * ctx, as this isn't available yet during parsing. + */ +void DetectLuaPostSetup(Signature *s) +{ + int i; + SigMatch *sm; + + for (i = 0; i < DETECT_SM_LIST_MAX; i++) { + for (sm = s->init_data->smlists[i]; sm != NULL; sm = sm->next) { + if (sm->type != DETECT_LUA) + continue; + + DetectLuaData *ld = (DetectLuaData *)sm->ctx; + ld->sid = s->id; + ld->rev = s->rev; + ld->gid = s->gid; + } + } +} + +/** + * \brief this function will free memory associated with DetectLuaData + * + * \param ptr pointer to DetectLuaData + */ +static void DetectLuaFree(DetectEngineCtx *de_ctx, void *ptr) +{ + if (ptr != NULL) { + DetectLuaData *lua = (DetectLuaData *)ptr; + + if (lua->buffername) + SCFree(lua->buffername); + if (lua->filename) + SCFree(lua->filename); + + for (uint16_t i = 0; i < lua->flowints; i++) { + VarNameStoreUnregister(lua->flowint[i], VAR_TYPE_FLOW_INT); + } + for (uint16_t i = 0; i < lua->flowvars; i++) { + VarNameStoreUnregister(lua->flowvar[i], VAR_TYPE_FLOW_VAR); + } + + DetectUnregisterThreadCtxFuncs(de_ctx, lua, "lua"); + + SCFree(lua); + } +} + +#ifdef UNITTESTS +#include "detect-engine-alert.h" + +/** \test http buffer */ +static int LuaMatchTest01(void) +{ + ConfSetFinal("security.lua.allow-rules", "true"); + + const char script[] = + "function init (args)\n" + " local needs = {}\n" + " needs[\"http.request_headers\"] = tostring(true)\n" + " needs[\"flowvar\"] = {\"cnt\"}\n" + " return needs\n" + "end\n" + "\n" + "function match(args)\n" + " a = ScFlowvarGet(0)\n" + " if a then\n" + " a = tostring(tonumber(a)+1)\n" + " print (a)\n" + " ScFlowvarSet(0, a, #a)\n" + " else\n" + " a = tostring(1)\n" + " print (a)\n" + " ScFlowvarSet(0, a, #a)\n" + " end\n" + " \n" + " print (\"pre check: \" .. (a))\n" + " if tonumber(a) == 2 then\n" + " print \"match\"\n" + " return 1\n" + " end\n" + " return 0\n" + "end\n" + "return 0\n"; + char sig[] = "alert http any any -> any any (flow:to_server; lua:unittest; sid:1;)"; + uint8_t httpbuf1[] = + "POST / HTTP/1.1\r\n" + "Host: www.emergingthreats.net\r\n\r\n"; + uint8_t httpbuf2[] = + "POST / HTTP/1.1\r\n" + "Host: www.openinfosecfoundation.org\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + TcpSession ssn; + Flow f; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + + ut_script = script; + + memset(&th_v, 0, sizeof(th_v)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + Packet *p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + Packet *p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.flags |= FLOW_IPV4; + f.alproto = ALPROTO_HTTP1; + + p1->flow = &f; + p1->flowflags |= FLOW_PKT_TOSERVER; + p1->flowflags |= FLOW_PKT_ESTABLISHED; + p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; + p2->flow = &f; + p2->flowflags |= FLOW_PKT_TOSERVER; + p2->flowflags |= FLOW_PKT_ESTABLISHED; + p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; + + StreamTcpInitConfig(true); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *s = DetectEngineAppendSig(de_ctx, sig); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + int r = AppLayerParserParse( + NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf1, httplen1); + FAIL_IF(r != 0); + HtpState *http_state = f.alstate; + FAIL_IF_NULL(http_state); + + /* do detect for p1 */ + SCLogDebug("inspecting p1"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + + FAIL_IF(PacketAlertCheck(p1, 1)); + + r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2); + FAIL_IF(r != 0); + + /* do detect for p2 */ + SCLogDebug("inspecting p2"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + + FAIL_IF_NOT(PacketAlertCheck(p2, 1)); + + uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_VAR); + FAIL_IF(id == 0); + + FlowVar *fv = FlowVarGet(&f, id); + FAIL_IF_NULL(fv); + + FAIL_IF(fv->data.fv_str.value_len != 1); + + FAIL_IF(memcmp(fv->data.fv_str.value, "2", 1) != 0); + + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(true); + FLOW_DESTROY(&f); + UTHFreePackets(&p1, 1); + UTHFreePackets(&p2, 1); + PASS; +} + +static int LuaMatchTest01a(void) +{ + const char script[] = "function init (args)\n" + " local needs = {}\n" + " needs[\"http.request_headers\"] = tostring(true)\n" + " needs[\"flowvar\"] = {\"cnt\"}\n" + " return needs\n" + "end\n" + "\n" + "function match(args)\n" + " a = SCFlowvarGet(0)\n" + " if a then\n" + " a = tostring(tonumber(a)+1)\n" + " print (a)\n" + " SCFlowvarSet(0, a, #a)\n" + " else\n" + " a = tostring(1)\n" + " print (a)\n" + " SCFlowvarSet(0, a, #a)\n" + " end\n" + " \n" + " print (\"pre check: \" .. (a))\n" + " if tonumber(a) == 2 then\n" + " print \"match\"\n" + " return 1\n" + " end\n" + " return 0\n" + "end\n" + "return 0\n"; + char sig[] = "alert http any any -> any any (flow:to_server; lua:unittest; sid:1;)"; + uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n" + "Host: www.emergingthreats.net\r\n\r\n"; + uint8_t httpbuf2[] = "POST / HTTP/1.1\r\n" + "Host: www.openinfosecfoundation.org\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + TcpSession ssn; + Flow f; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + + ut_script = script; + + memset(&th_v, 0, sizeof(th_v)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + Packet *p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + Packet *p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.flags |= FLOW_IPV4; + f.alproto = ALPROTO_HTTP1; + + p1->flow = &f; + p1->flowflags |= FLOW_PKT_TOSERVER; + p1->flowflags |= FLOW_PKT_ESTABLISHED; + p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + p2->flow = &f; + p2->flowflags |= FLOW_PKT_TOSERVER; + p2->flowflags |= FLOW_PKT_ESTABLISHED; + p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + + StreamTcpInitConfig(true); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *s = DetectEngineAppendSig(de_ctx, sig); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + int r = AppLayerParserParse( + NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf1, httplen1); + FAIL_IF(r != 0); + + HtpState *http_state = f.alstate; + FAIL_IF_NULL(http_state); + + /* do detect for p1 */ + SCLogDebug("inspecting p1"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + + FAIL_IF(PacketAlertCheck(p1, 1)); + + r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2); + FAIL_IF(r != 0); + /* do detect for p2 */ + SCLogDebug("inspecting p2"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + + FAIL_IF_NOT(PacketAlertCheck(p2, 1)); + + uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_VAR); + FAIL_IF(id == 0); + + FlowVar *fv = FlowVarGet(&f, id); + FAIL_IF_NULL(fv); + + FAIL_IF(fv->data.fv_str.value_len != 1); + + FAIL_IF(memcmp(fv->data.fv_str.value, "2", 1) != 0); + + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(true); + FLOW_DESTROY(&f); + UTHFreePackets(&p1, 1); + UTHFreePackets(&p2, 1); + PASS; +} + +/** \test payload buffer */ +static int LuaMatchTest02(void) +{ + const char script[] = "function init (args)\n" + " local needs = {}\n" + " needs[\"payload\"] = tostring(true)\n" + " needs[\"flowvar\"] = {\"cnt\"}\n" + " return needs\n" + "end\n" + "\n" + "function match(args)\n" + " a = ScFlowvarGet(0)\n" + " if a then\n" + " a = tostring(tonumber(a)+1)\n" + " print (a)\n" + " ScFlowvarSet(0, a, #a)\n" + " else\n" + " a = tostring(1)\n" + " print (a)\n" + " ScFlowvarSet(0, a, #a)\n" + " end\n" + " \n" + " print (\"pre check: \" .. (a))\n" + " if tonumber(a) == 2 then\n" + " print \"match\"\n" + " return 1\n" + " end\n" + " return 0\n" + "end\n" + "return 0\n"; + char sig[] = "alert tcp any any -> any any (flow:to_server; lua:unittest; sid:1;)"; + uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n" + "Host: www.emergingthreats.net\r\n\r\n"; + uint8_t httpbuf2[] = "POST / HTTP/1.1\r\n" + "Host: www.openinfosecfoundation.org\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + TcpSession ssn; + Flow f; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + ut_script = script; + + memset(&th_v, 0, sizeof(th_v)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + Packet *p1 = UTHBuildPacket(httpbuf1, httplen1, IPPROTO_TCP); + Packet *p2 = UTHBuildPacket(httpbuf2, httplen2, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.flags |= FLOW_IPV4; + f.alproto = ALPROTO_HTTP1; + + p1->flow = &f; + p1->flowflags |= FLOW_PKT_TOSERVER; + p1->flowflags |= FLOW_PKT_ESTABLISHED; + p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + p2->flow = &f; + p2->flowflags |= FLOW_PKT_TOSERVER; + p2->flowflags |= FLOW_PKT_ESTABLISHED; + p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + + StreamTcpInitConfig(true); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *s = DetectEngineAppendSig(de_ctx, sig); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + /* do detect for p1 */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + + FAIL_IF(PacketAlertCheck(p1, 1)); + + /* do detect for p2 */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + + FAIL_IF_NOT(PacketAlertCheck(p2, 1)); + + uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_VAR); + FAIL_IF(id == 0); + + FlowVar *fv = FlowVarGet(&f, id); + FAIL_IF_NULL(fv); + + FAIL_IF(fv->data.fv_str.value_len != 1); + + FAIL_IF(memcmp(fv->data.fv_str.value, "2", 1) != 0); + + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(true); + FLOW_DESTROY(&f); + UTHFreePackets(&p1, 1); + UTHFreePackets(&p2, 1); + PASS; +} + +/** \test payload buffer */ +static int LuaMatchTest02a(void) +{ + const char script[] = "function init (args)\n" + " local needs = {}\n" + " needs[\"payload\"] = tostring(true)\n" + " needs[\"flowvar\"] = {\"cnt\"}\n" + " return needs\n" + "end\n" + "\n" + "function match(args)\n" + " a = SCFlowvarGet(0)\n" + " if a then\n" + " a = tostring(tonumber(a)+1)\n" + " print (a)\n" + " SCFlowvarSet(0, a, #a)\n" + " else\n" + " a = tostring(1)\n" + " print (a)\n" + " SCFlowvarSet(0, a, #a)\n" + " end\n" + " \n" + " print (\"pre check: \" .. (a))\n" + " if tonumber(a) == 2 then\n" + " print \"match\"\n" + " return 1\n" + " end\n" + " return 0\n" + "end\n" + "return 0\n"; + char sig[] = "alert tcp any any -> any any (flow:to_server; lua:unittest; sid:1;)"; + uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n" + "Host: www.emergingthreats.net\r\n\r\n"; + uint8_t httpbuf2[] = "POST / HTTP/1.1\r\n" + "Host: www.openinfosecfoundation.org\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + TcpSession ssn; + Flow f; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + ut_script = script; + + memset(&th_v, 0, sizeof(th_v)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + Packet *p1 = UTHBuildPacket(httpbuf1, httplen1, IPPROTO_TCP); + Packet *p2 = UTHBuildPacket(httpbuf2, httplen2, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.flags |= FLOW_IPV4; + f.alproto = ALPROTO_HTTP1; + + p1->flow = &f; + p1->flowflags |= FLOW_PKT_TOSERVER; + p1->flowflags |= FLOW_PKT_ESTABLISHED; + p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + p2->flow = &f; + p2->flowflags |= FLOW_PKT_TOSERVER; + p2->flowflags |= FLOW_PKT_ESTABLISHED; + p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + + StreamTcpInitConfig(true); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *s = DetectEngineAppendSig(de_ctx, sig); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + /* do detect for p1 */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + FAIL_IF(PacketAlertCheck(p1, 1)); + + /* do detect for p2 */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + + FAIL_IF_NOT(PacketAlertCheck(p2, 1)); + + uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_VAR); + FAIL_IF(id == 0); + + FlowVar *fv = FlowVarGet(&f, id); + FAIL_IF_NULL(fv); + + FAIL_IF(fv->data.fv_str.value_len != 1); + + FAIL_IF(memcmp(fv->data.fv_str.value, "2", 1) != 0); + + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(true); + FLOW_DESTROY(&f); + UTHFreePackets(&p1, 1); + UTHFreePackets(&p2, 1); + PASS; +} + +/** \test packet buffer */ +static int LuaMatchTest03(void) +{ + const char script[] = "function init (args)\n" + " local needs = {}\n" + " needs[\"packet\"] = tostring(true)\n" + " needs[\"flowvar\"] = {\"cnt\"}\n" + " return needs\n" + "end\n" + "\n" + "function match(args)\n" + " a = ScFlowvarGet(0)\n" + " if a then\n" + " a = tostring(tonumber(a)+1)\n" + " print (a)\n" + " ScFlowvarSet(0, a, #a)\n" + " else\n" + " a = tostring(1)\n" + " print (a)\n" + " ScFlowvarSet(0, a, #a)\n" + " end\n" + " \n" + " print (\"pre check: \" .. (a))\n" + " if tonumber(a) == 2 then\n" + " print \"match\"\n" + " return 1\n" + " end\n" + " return 0\n" + "end\n" + "return 0\n"; + char sig[] = "alert tcp any any -> any any (flow:to_server; lua:unittest; sid:1;)"; + uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n" + "Host: www.emergingthreats.net\r\n\r\n"; + uint8_t httpbuf2[] = "POST / HTTP/1.1\r\n" + "Host: www.openinfosecfoundation.org\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + TcpSession ssn; + Flow f; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + ut_script = script; + + memset(&th_v, 0, sizeof(th_v)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + Packet *p1 = UTHBuildPacket(httpbuf1, httplen1, IPPROTO_TCP); + Packet *p2 = UTHBuildPacket(httpbuf2, httplen2, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.flags |= FLOW_IPV4; + f.alproto = ALPROTO_HTTP1; + + p1->flow = &f; + p1->flowflags |= FLOW_PKT_TOSERVER; + p1->flowflags |= FLOW_PKT_ESTABLISHED; + p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + p2->flow = &f; + p2->flowflags |= FLOW_PKT_TOSERVER; + p2->flowflags |= FLOW_PKT_ESTABLISHED; + p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + + StreamTcpInitConfig(true); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *s = DetectEngineAppendSig(de_ctx, sig); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + /* do detect for p1 */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + FAIL_IF(PacketAlertCheck(p1, 1)); + + /* do detect for p2 */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + + FAIL_IF_NOT(PacketAlertCheck(p2, 1)); + + uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_VAR); + FAIL_IF(id == 0); + + FlowVar *fv = FlowVarGet(&f, id); + FAIL_IF_NULL(fv); + + FAIL_IF(fv->data.fv_str.value_len != 1); + + FAIL_IF(memcmp(fv->data.fv_str.value, "2", 1) != 0); + + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(true); + FLOW_DESTROY(&f); + UTHFreePackets(&p1, 1); + UTHFreePackets(&p2, 1); + PASS; +} + +/** \test packet buffer */ +static int LuaMatchTest03a(void) +{ + const char script[] = "function init (args)\n" + " local needs = {}\n" + " needs[\"packet\"] = tostring(true)\n" + " needs[\"flowvar\"] = {\"cnt\"}\n" + " return needs\n" + "end\n" + "\n" + "function match(args)\n" + " a = SCFlowvarGet(0)\n" + " if a then\n" + " a = tostring(tonumber(a)+1)\n" + " print (a)\n" + " SCFlowvarSet(0, a, #a)\n" + " else\n" + " a = tostring(1)\n" + " print (a)\n" + " SCFlowvarSet(0, a, #a)\n" + " end\n" + " \n" + " print (\"pre check: \" .. (a))\n" + " if tonumber(a) == 2 then\n" + " print \"match\"\n" + " return 1\n" + " end\n" + " return 0\n" + "end\n" + "return 0\n"; + char sig[] = "alert tcp any any -> any any (flow:to_server; lua:unittest; sid:1;)"; + uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n" + "Host: www.emergingthreats.net\r\n\r\n"; + uint8_t httpbuf2[] = "POST / HTTP/1.1\r\n" + "Host: www.openinfosecfoundation.org\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + TcpSession ssn; + Flow f; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + ut_script = script; + + memset(&th_v, 0, sizeof(th_v)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + Packet *p1 = UTHBuildPacket(httpbuf1, httplen1, IPPROTO_TCP); + Packet *p2 = UTHBuildPacket(httpbuf2, httplen2, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.flags |= FLOW_IPV4; + f.alproto = ALPROTO_HTTP1; + + p1->flow = &f; + p1->flowflags |= FLOW_PKT_TOSERVER; + p1->flowflags |= FLOW_PKT_ESTABLISHED; + p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + p2->flow = &f; + p2->flowflags |= FLOW_PKT_TOSERVER; + p2->flowflags |= FLOW_PKT_ESTABLISHED; + p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + + StreamTcpInitConfig(true); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *s = DetectEngineAppendSig(de_ctx, sig); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + /* do detect for p1 */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + FAIL_IF(PacketAlertCheck(p1, 1)); + + /* do detect for p2 */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + FAIL_IF_NOT(PacketAlertCheck(p2, 1)); + + uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_VAR); + FAIL_IF(id == 0); + + FlowVar *fv = FlowVarGet(&f, id); + FAIL_IF_NULL(fv); + + FAIL_IF(fv->data.fv_str.value_len != 1); + + FAIL_IF(memcmp(fv->data.fv_str.value, "2", 1) != 0); + + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(true); + FLOW_DESTROY(&f); + UTHFreePackets(&p1, 1); + UTHFreePackets(&p2, 1); + PASS; +} + +/** \test http buffer, flowints */ +static int LuaMatchTest04(void) +{ + const char script[] = "function init (args)\n" + " local needs = {}\n" + " needs[\"http.request_headers\"] = tostring(true)\n" + " needs[\"flowint\"] = {\"cnt\"}\n" + " return needs\n" + "end\n" + "\n" + "function match(args)\n" + " print \"inspecting\"" + " a = ScFlowintGet(0)\n" + " if a then\n" + " ScFlowintSet(0, a + 1)\n" + " else\n" + " ScFlowintSet(0, 1)\n" + " end\n" + " \n" + " a = ScFlowintGet(0)\n" + " if a == 2 then\n" + " print \"match\"\n" + " return 1\n" + " end\n" + " return 0\n" + "end\n" + "return 0\n"; + char sig[] = "alert http any any -> any any (flow:to_server; lua:unittest; sid:1;)"; + uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n" + "Host: www.emergingthreats.net\r\n\r\n"; + uint8_t httpbuf2[] = "POST / HTTP/1.1\r\n" + "Host: www.openinfosecfoundation.org\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + TcpSession ssn; + Flow f; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + + ut_script = script; + + memset(&th_v, 0, sizeof(th_v)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + Packet *p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + Packet *p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.flags |= FLOW_IPV4; + f.alproto = ALPROTO_HTTP1; + + p1->flow = &f; + p1->flowflags |= FLOW_PKT_TOSERVER; + p1->flowflags |= FLOW_PKT_ESTABLISHED; + p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + + p2->flow = &f; + p2->flowflags |= FLOW_PKT_TOSERVER; + p2->flowflags |= FLOW_PKT_ESTABLISHED; + p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + + StreamTcpInitConfig(true); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *s = DetectEngineAppendSig(de_ctx, sig); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + int r = AppLayerParserParse( + NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf1, httplen1); + FAIL_IF(r != 0); + HtpState *http_state = f.alstate; + FAIL_IF_NULL(http_state); + + /* do detect for p1 */ + SCLogInfo("p1"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + + FAIL_IF(PacketAlertCheck(p1, 1)); + + r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2); + FAIL_IF(r != 0); + /* do detect for p2 */ + SCLogInfo("p2"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + + FAIL_IF_NOT(PacketAlertCheck(p2, 1)); + + uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_INT); + FAIL_IF(id == 0); + + FlowVar *fv = FlowVarGet(&f, id); + FAIL_IF_NULL(fv); + + FAIL_IF(fv->data.fv_int.value != 2); + + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(true); + FLOW_DESTROY(&f); + UTHFreePackets(&p1, 1); + UTHFreePackets(&p2, 1); + PASS; +} + +/** \test http buffer, flowints */ +static int LuaMatchTest04a(void) +{ + const char script[] = "function init (args)\n" + " local needs = {}\n" + " needs[\"http.request_headers\"] = tostring(true)\n" + " needs[\"flowint\"] = {\"cnt\"}\n" + " return needs\n" + "end\n" + "\n" + "function match(args)\n" + " print \"inspecting\"" + " a = SCFlowintGet(0)\n" + " if a then\n" + " SCFlowintSet(0, a + 1)\n" + " else\n" + " SCFlowintSet(0, 1)\n" + " end\n" + " \n" + " a = SCFlowintGet(0)\n" + " if a == 2 then\n" + " print \"match\"\n" + " return 1\n" + " end\n" + " return 0\n" + "end\n" + "return 0\n"; + char sig[] = "alert http any any -> any any (flow:to_server; lua:unittest; sid:1;)"; + uint8_t httpbuf1[] = + "POST / HTTP/1.1\r\n" + "Host: www.emergingthreats.net\r\n\r\n"; + uint8_t httpbuf2[] = + "POST / HTTP/1.1\r\n" + "Host: www.openinfosecfoundation.org\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + TcpSession ssn; + Flow f; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + + ut_script = script; + + memset(&th_v, 0, sizeof(th_v)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + Packet *p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + Packet *p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.flags |= FLOW_IPV4; + f.alproto = ALPROTO_HTTP1; + + p1->flow = &f; + p1->flowflags |= FLOW_PKT_TOSERVER; + p1->flowflags |= FLOW_PKT_ESTABLISHED; + p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; + + p2->flow = &f; + p2->flowflags |= FLOW_PKT_TOSERVER; + p2->flowflags |= FLOW_PKT_ESTABLISHED; + p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; + + StreamTcpInitConfig(true); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *s = DetectEngineAppendSig(de_ctx, sig); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + int r = AppLayerParserParse( + NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf1, httplen1); + FAIL_IF(r != 0); + HtpState *http_state = f.alstate; + FAIL_IF_NULL(http_state); + + /* do detect for p1 */ + SCLogInfo("p1"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + + FAIL_IF(PacketAlertCheck(p1, 1)); + + r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2); + FAIL_IF(r != 0); + /* do detect for p2 */ + SCLogInfo("p2"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + + FAIL_IF_NOT(PacketAlertCheck(p2, 1)); + + uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_INT); + FAIL_IF(id == 0); + + FlowVar *fv = FlowVarGet(&f, id); + FAIL_IF_NULL(fv); + + FAIL_IF(fv->data.fv_int.value != 2); + + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(true); + FLOW_DESTROY(&f); + UTHFreePackets(&p1, 1); + UTHFreePackets(&p2, 1); + PASS; +} + +/** \test http buffer, flowints */ +static int LuaMatchTest05(void) +{ + const char script[] = "function init (args)\n" + " local needs = {}\n" + " needs[\"http.request_headers\"] = tostring(true)\n" + " needs[\"flowint\"] = {\"cnt\"}\n" + " return needs\n" + "end\n" + "\n" + "function match(args)\n" + " print \"inspecting\"" + " a = ScFlowintIncr(0)\n" + " if a == 2 then\n" + " print \"match\"\n" + " return 1\n" + " end\n" + " return 0\n" + "end\n" + "return 0\n"; + char sig[] = "alert http any any -> any any (flow:to_server; lua:unittest; sid:1;)"; + uint8_t httpbuf1[] = + "POST / HTTP/1.1\r\n" + "Host: www.emergingthreats.net\r\n\r\n"; + uint8_t httpbuf2[] = + "POST / HTTP/1.1\r\n" + "Host: www.openinfosecfoundation.org\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + TcpSession ssn; + Flow f; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + + ut_script = script; + + memset(&th_v, 0, sizeof(th_v)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + Packet *p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + Packet *p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.flags |= FLOW_IPV4; + f.alproto = ALPROTO_HTTP1; + + p1->flow = &f; + p1->flowflags |= FLOW_PKT_TOSERVER; + p1->flowflags |= FLOW_PKT_ESTABLISHED; + p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; + + p2->flow = &f; + p2->flowflags |= FLOW_PKT_TOSERVER; + p2->flowflags |= FLOW_PKT_ESTABLISHED; + p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; + + StreamTcpInitConfig(true); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *s = DetectEngineAppendSig(de_ctx, sig); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + int r = AppLayerParserParse( + NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf1, httplen1); + FAIL_IF(r != 0); + HtpState *http_state = f.alstate; + FAIL_IF_NULL(http_state); + + /* do detect for p1 */ + SCLogInfo("p1"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + + FAIL_IF(PacketAlertCheck(p1, 1)); + + r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2); + FAIL_IF(r != 0); + /* do detect for p2 */ + SCLogInfo("p2"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + + FAIL_IF_NOT(PacketAlertCheck(p2, 1)); + + uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_INT); + FAIL_IF(id == 0); + + FlowVar *fv = FlowVarGet(&f, id); + FAIL_IF_NULL(fv); + + FAIL_IF(fv->data.fv_int.value != 2); + + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(true); + FLOW_DESTROY(&f); + UTHFreePackets(&p1, 1); + UTHFreePackets(&p2, 1); + PASS; +} + +/** \test http buffer, flowints */ +static int LuaMatchTest05a(void) +{ + const char script[] = "function init (args)\n" + " local needs = {}\n" + " needs[\"http.request_headers\"] = tostring(true)\n" + " needs[\"flowint\"] = {\"cnt\"}\n" + " return needs\n" + "end\n" + "\n" + "function match(args)\n" + " print \"inspecting\"" + " a = SCFlowintIncr(0)\n" + " if a == 2 then\n" + " print \"match\"\n" + " return 1\n" + " end\n" + " return 0\n" + "end\n" + "return 0\n"; + char sig[] = "alert http any any -> any any (flow:to_server; lua:unittest; sid:1;)"; + uint8_t httpbuf1[] = + "POST / HTTP/1.1\r\n" + "Host: www.emergingthreats.net\r\n\r\n"; + uint8_t httpbuf2[] = + "POST / HTTP/1.1\r\n" + "Host: www.openinfosecfoundation.org\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + TcpSession ssn; + Flow f; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + + ut_script = script; + + memset(&th_v, 0, sizeof(th_v)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + Packet *p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + Packet *p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.flags |= FLOW_IPV4; + f.alproto = ALPROTO_HTTP1; + + p1->flow = &f; + p1->flowflags |= FLOW_PKT_TOSERVER; + p1->flowflags |= FLOW_PKT_ESTABLISHED; + p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; + + p2->flow = &f; + p2->flowflags |= FLOW_PKT_TOSERVER; + p2->flowflags |= FLOW_PKT_ESTABLISHED; + p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; + + StreamTcpInitConfig(true); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *s = DetectEngineAppendSig(de_ctx, sig); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + int r = AppLayerParserParse( + NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf1, httplen1); + FAIL_IF(r != 0); + HtpState *http_state = f.alstate; + FAIL_IF_NULL(http_state); + + /* do detect for p1 */ + SCLogInfo("p1"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + + FAIL_IF(PacketAlertCheck(p1, 1)); + + r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2); + FAIL_IF(r != 0); + /* do detect for p2 */ + SCLogInfo("p2"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + + FAIL_IF_NOT(PacketAlertCheck(p2, 1)); + + uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_INT); + FAIL_IF(id == 0); + + FlowVar *fv = FlowVarGet(&f, id); + FAIL_IF_NULL(fv); + + FAIL_IF(fv->data.fv_int.value != 2); + + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(true); + FLOW_DESTROY(&f); + UTHFreePackets(&p1, 1); + UTHFreePackets(&p2, 1); + PASS; +} + +/** \test http buffer, flowints */ +static int LuaMatchTest06(void) +{ + const char script[] = "function init (args)\n" + " local needs = {}\n" + " needs[\"http.request_headers\"] = tostring(true)\n" + " needs[\"flowint\"] = {\"cnt\"}\n" + " return needs\n" + "end\n" + "\n" + "function match(args)\n" + " print \"inspecting\"" + " a = ScFlowintGet(0)\n" + " if a == nil then\n" + " print \"new var set to 2\"" + " ScFlowintSet(0, 2)\n" + " end\n" + " a = ScFlowintDecr(0)\n" + " if a == 0 then\n" + " print \"match\"\n" + " return 1\n" + " end\n" + " return 0\n" + "end\n" + "return 0\n"; + char sig[] = "alert http any any -> any any (flow:to_server; lua:unittest; sid:1;)"; + uint8_t httpbuf1[] = + "POST / HTTP/1.1\r\n" + "Host: www.emergingthreats.net\r\n\r\n"; + uint8_t httpbuf2[] = + "POST / HTTP/1.1\r\n" + "Host: www.openinfosecfoundation.org\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + TcpSession ssn; + Flow f; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + + ut_script = script; + + memset(&th_v, 0, sizeof(th_v)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + Packet *p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + Packet *p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.flags |= FLOW_IPV4; + f.alproto = ALPROTO_HTTP1; + + p1->flow = &f; + p1->flowflags |= FLOW_PKT_TOSERVER; + p1->flowflags |= FLOW_PKT_ESTABLISHED; + p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; + + p2->flow = &f; + p2->flowflags |= FLOW_PKT_TOSERVER; + p2->flowflags |= FLOW_PKT_ESTABLISHED; + p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; + + StreamTcpInitConfig(true); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *s = DetectEngineAppendSig(de_ctx, sig); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + int r = AppLayerParserParse( + NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf1, httplen1); + FAIL_IF(r != 0); + HtpState *http_state = f.alstate; + FAIL_IF_NULL(http_state); + + /* do detect for p1 */ + SCLogInfo("p1"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + + FAIL_IF(PacketAlertCheck(p1, 1)); + + r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2); + FAIL_IF(r != 0); + /* do detect for p2 */ + SCLogInfo("p2"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + + FAIL_IF_NOT(PacketAlertCheck(p2, 1)); + + uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_INT); + FAIL_IF(id == 0); + + FlowVar *fv = FlowVarGet(&f, id); + FAIL_IF_NULL(fv); + + FAIL_IF(fv->data.fv_int.value != 0); + + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(true); + FLOW_DESTROY(&f); + UTHFreePackets(&p1, 1); + UTHFreePackets(&p2, 1); + PASS; +} + +/** \test http buffer, flowints */ +static int LuaMatchTest06a(void) +{ + const char script[] = "function init (args)\n" + " local needs = {}\n" + " needs[\"http.request_headers\"] = tostring(true)\n" + " needs[\"flowint\"] = {\"cnt\"}\n" + " return needs\n" + "end\n" + "\n" + "function match(args)\n" + " print \"inspecting\"" + " a = SCFlowintGet(0)\n" + " if a == nil then\n" + " print \"new var set to 2\"" + " SCFlowintSet(0, 2)\n" + " end\n" + " a = SCFlowintDecr(0)\n" + " if a == 0 then\n" + " print \"match\"\n" + " return 1\n" + " end\n" + " return 0\n" + "end\n" + "return 0\n"; + char sig[] = "alert http any any -> any any (flow:to_server; lua:unittest; sid:1;)"; + uint8_t httpbuf1[] = + "POST / HTTP/1.1\r\n" + "Host: www.emergingthreats.net\r\n\r\n"; + uint8_t httpbuf2[] = + "POST / HTTP/1.1\r\n" + "Host: www.openinfosecfoundation.org\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + TcpSession ssn; + Flow f; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + + ut_script = script; + + memset(&th_v, 0, sizeof(th_v)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + Packet *p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + Packet *p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.flags |= FLOW_IPV4; + f.alproto = ALPROTO_HTTP1; + + p1->flow = &f; + p1->flowflags |= FLOW_PKT_TOSERVER; + p1->flowflags |= FLOW_PKT_ESTABLISHED; + p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; + + p2->flow = &f; + p2->flowflags |= FLOW_PKT_TOSERVER; + p2->flowflags |= FLOW_PKT_ESTABLISHED; + p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; + + StreamTcpInitConfig(true); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *s = DetectEngineAppendSig(de_ctx, sig); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + int r = AppLayerParserParse( + NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf1, httplen1); + FAIL_IF(r != 0); + HtpState *http_state = f.alstate; + FAIL_IF_NULL(http_state); + + /* do detect for p1 */ + SCLogInfo("p1"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + + FAIL_IF(PacketAlertCheck(p1, 1)); + + r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2); + FAIL_IF(r != 0); + /* do detect for p2 */ + SCLogInfo("p2"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + + FAIL_IF_NOT(PacketAlertCheck(p2, 1)); + + uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_INT); + FAIL_IF(id == 0); + + FlowVar *fv = FlowVarGet(&f, id); + FAIL_IF_NULL(fv); + + FAIL_IF(fv->data.fv_int.value != 0); + + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(true); + FLOW_DESTROY(&f); + UTHFreePackets(&p1, 1); + UTHFreePackets(&p2, 1); + PASS; +} + +void DetectLuaRegisterTests(void) +{ + UtRegisterTest("LuaMatchTest01", LuaMatchTest01); + UtRegisterTest("LuaMatchTest01a", LuaMatchTest01a); + UtRegisterTest("LuaMatchTest02", LuaMatchTest02); + UtRegisterTest("LuaMatchTest02a", LuaMatchTest02a); + UtRegisterTest("LuaMatchTest03", LuaMatchTest03); + UtRegisterTest("LuaMatchTest03a", LuaMatchTest03a); + UtRegisterTest("LuaMatchTest04", LuaMatchTest04); + UtRegisterTest("LuaMatchTest04a", LuaMatchTest04a); + UtRegisterTest("LuaMatchTest05", LuaMatchTest05); + UtRegisterTest("LuaMatchTest05a", LuaMatchTest05a); + UtRegisterTest("LuaMatchTest06", LuaMatchTest06); + UtRegisterTest("LuaMatchTest06a", LuaMatchTest06a); +} +#endif +#endif /* HAVE_LUAJIT */ |