/* 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 * */ #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 */