From f29e5459e68bf8e1a5614e772953748be6425451 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Tue, 23 Apr 2013 11:58:49 +0200 Subject: [PATCH] luajit/flowint: add ScFlowintIncr & ScFlowintDecr Add flowint lua functions for incrementing and decrementing flowints. First use creates the var and inits to 0. So a call: a = ScFlowintIncr(0) Results in a == 1. If the var reached UINT_MAX (2^32), it's not further incremented. If the var reaches 0 it's not decremented further. Calling ScFlowintDecr on a uninitialized var will init it to 0. Example script: function init (args) local needs = {} needs["http.request_headers"] = tostring(true) needs["flowint"] = {"cnt_incr"} return needs end function match(args) a = ScFlowintIncr(0); if a == 23 then return 1 end return 0 end return 0 This script matches the 23rd time it's invoked on a flow. --- src/detect-luajit-extensions.c | 185 ++++++++++++++++++++- src/detect-luajit.c | 295 +++++++++++++++++++++++++++++++++ 2 files changed, 474 insertions(+), 6 deletions(-) diff --git a/src/detect-luajit-extensions.c b/src/detect-luajit-extensions.c index 74b0637cc4..ffa708d624 100644 --- a/src/detect-luajit-extensions.c +++ b/src/detect-luajit-extensions.c @@ -304,7 +304,7 @@ static int LuajitGetFlowint(lua_State *luastate) { lua_gettable(luastate, LUA_REGISTRYINDEX); need_flow_lock = lua_toboolean(luastate, -1); - /* need flowvar idx */ + /* need flowint idx */ if (!lua_isnumber(luastate, 1)) { SCLogDebug("1st arg not a number"); lua_pushnil(luastate); @@ -315,14 +315,14 @@ static int LuajitGetFlowint(lua_State *luastate) { if (id < 0 || id >= DETECT_LUAJIT_MAX_FLOWINTS) { SCLogDebug("id %d", id); lua_pushnil(luastate); - lua_pushstring(luastate, "flowvar id out of range"); + lua_pushstring(luastate, "flowint id out of range"); return 2; } idx = ld->flowint[id]; if (idx == 0) { SCLogDebug("idx %u", idx); lua_pushnil(luastate); - lua_pushstring(luastate, "flowvar id uninitialized"); + lua_pushstring(luastate, "flowint id uninitialized"); return 2; } @@ -345,7 +345,7 @@ static int LuajitGetFlowint(lua_State *luastate) { if (need_flow_lock) FLOWLOCK_UNLOCK(f); - /* return value through luastate, as a luastring */ + /* return value through luastate, as a luanumber */ lua_pushnumber(luastate, (lua_Number)number); SCLogDebug("retrieved flow:%p idx:%u value:%u", f, idx, number); @@ -400,7 +400,7 @@ int LuajitSetFlowint(lua_State *luastate) { lua_gettable(luastate, LUA_REGISTRYINDEX); need_flow_lock = lua_toboolean(luastate, -1); - /* need flowvar idx */ + /* need flowint idx */ if (!lua_isnumber(luastate, 1)) { lua_pushnil(luastate); lua_pushstring(luastate, "1st arg not a number"); @@ -409,7 +409,7 @@ int LuajitSetFlowint(lua_State *luastate) { id = lua_tonumber(luastate, 1); if (id < 0 || id >= DETECT_LUAJIT_MAX_FLOWVARS) { lua_pushnil(luastate); - lua_pushstring(luastate, "flowvar id out of range"); + lua_pushstring(luastate, "flowint id out of range"); return 2; } @@ -442,6 +442,172 @@ int LuajitSetFlowint(lua_State *luastate) { return 0; } +static int LuajitIncrFlowint(lua_State *luastate) { + uint16_t idx; + int id; + Flow *f; + FlowVar *fv; + DetectLuajitData *ld; + int need_flow_lock = 0; + uint32_t number; + + /* need luajit data for id -> idx conversion */ + lua_pushlightuserdata(luastate, (void *)&luaext_key_ld); + lua_gettable(luastate, LUA_REGISTRYINDEX); + ld = lua_touserdata(luastate, -1); + SCLogDebug("ld %p", ld); + if (ld == NULL) { + lua_pushnil(luastate); + lua_pushstring(luastate, "internal error: no ld"); + return 2; + } + + /* need flow */ + lua_pushlightuserdata(luastate, (void *)&luaext_key_flow); + lua_gettable(luastate, LUA_REGISTRYINDEX); + f = lua_touserdata(luastate, -1); + SCLogDebug("f %p", f); + if (f == NULL) { + lua_pushnil(luastate); + lua_pushstring(luastate, "no flow"); + return 2; + } + + /* need flow lock hint */ + lua_pushlightuserdata(luastate, (void *)&luaext_key_need_flow_lock); + lua_gettable(luastate, LUA_REGISTRYINDEX); + need_flow_lock = lua_toboolean(luastate, -1); + + /* need flowint idx */ + if (!lua_isnumber(luastate, 1)) { + SCLogDebug("1st arg not a number"); + lua_pushnil(luastate); + lua_pushstring(luastate, "1st arg not a number"); + return 2; + } + id = lua_tonumber(luastate, 1); + if (id < 0 || id >= DETECT_LUAJIT_MAX_FLOWINTS) { + SCLogDebug("id %d", id); + lua_pushnil(luastate); + lua_pushstring(luastate, "flowint id out of range"); + return 2; + } + idx = ld->flowint[id]; + if (idx == 0) { + SCLogDebug("idx %u", idx); + lua_pushnil(luastate); + lua_pushstring(luastate, "flowint id uninitialized"); + return 2; + } + + /* lookup var */ + if (need_flow_lock) + FLOWLOCK_RDLOCK(f); + + fv = FlowVarGet(f, idx); + if (fv == NULL) { + number = 1; + } else { + number = fv->data.fv_int.value; + if (number < UINT_MAX) + number++; + } + FlowVarAddIntNoLock(f, idx, number); + + if (need_flow_lock) + FLOWLOCK_UNLOCK(f); + + /* return value through luastate, as a luanumber */ + lua_pushnumber(luastate, (lua_Number)number); + SCLogDebug("incremented flow:%p idx:%u value:%u", f, idx, number); + + return 1; + +} + +static int LuajitDecrFlowint(lua_State *luastate) { + uint16_t idx; + int id; + Flow *f; + FlowVar *fv; + DetectLuajitData *ld; + int need_flow_lock = 0; + uint32_t number; + + /* need luajit data for id -> idx conversion */ + lua_pushlightuserdata(luastate, (void *)&luaext_key_ld); + lua_gettable(luastate, LUA_REGISTRYINDEX); + ld = lua_touserdata(luastate, -1); + SCLogDebug("ld %p", ld); + if (ld == NULL) { + lua_pushnil(luastate); + lua_pushstring(luastate, "internal error: no ld"); + return 2; + } + + /* need flow */ + lua_pushlightuserdata(luastate, (void *)&luaext_key_flow); + lua_gettable(luastate, LUA_REGISTRYINDEX); + f = lua_touserdata(luastate, -1); + SCLogDebug("f %p", f); + if (f == NULL) { + lua_pushnil(luastate); + lua_pushstring(luastate, "no flow"); + return 2; + } + + /* need flow lock hint */ + lua_pushlightuserdata(luastate, (void *)&luaext_key_need_flow_lock); + lua_gettable(luastate, LUA_REGISTRYINDEX); + need_flow_lock = lua_toboolean(luastate, -1); + + /* need flowint idx */ + if (!lua_isnumber(luastate, 1)) { + SCLogDebug("1st arg not a number"); + lua_pushnil(luastate); + lua_pushstring(luastate, "1st arg not a number"); + return 2; + } + id = lua_tonumber(luastate, 1); + if (id < 0 || id >= DETECT_LUAJIT_MAX_FLOWINTS) { + SCLogDebug("id %d", id); + lua_pushnil(luastate); + lua_pushstring(luastate, "flowint id out of range"); + return 2; + } + idx = ld->flowint[id]; + if (idx == 0) { + SCLogDebug("idx %u", idx); + lua_pushnil(luastate); + lua_pushstring(luastate, "flowint id uninitialized"); + return 2; + } + + /* lookup var */ + if (need_flow_lock) + FLOWLOCK_RDLOCK(f); + + fv = FlowVarGet(f, idx); + if (fv == NULL) { + number = 0; + } else { + number = fv->data.fv_int.value; + if (number > 0) + number--; + } + FlowVarAddIntNoLock(f, idx, number); + + if (need_flow_lock) + FLOWLOCK_UNLOCK(f); + + /* return value through luastate, as a luanumber */ + lua_pushnumber(luastate, (lua_Number)number); + SCLogDebug("decremented flow:%p idx:%u value:%u", f, idx, number); + + return 1; + +} + void LuajitExtensionsMatchSetup(lua_State *lua_state, DetectLuajitData *ld, DetectEngineThreadCtx *det_ctx, Flow *f, int need_flow_lock) { SCLogDebug("det_ctx %p, f %p", det_ctx, f); @@ -481,6 +647,13 @@ int LuajitRegisterExtensions(lua_State *lua_state) { lua_pushcfunction(lua_state, LuajitSetFlowint); lua_setglobal(lua_state, "ScFlowintSet"); + + lua_pushcfunction(lua_state, LuajitIncrFlowint); + lua_setglobal(lua_state, "ScFlowintIncr"); + + lua_pushcfunction(lua_state, LuajitDecrFlowint); + lua_setglobal(lua_state, "ScFlowintDecr"); + return 0; } diff --git a/src/detect-luajit.c b/src/detect-luajit.c index 0299fce519..c22ec29da6 100644 --- a/src/detect-luajit.c +++ b/src/detect-luajit.c @@ -1475,6 +1475,299 @@ end: return result; } +/** \test http buffer, flowints */ +static int LuajitMatchTest05(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; luajit:unittest; sid:1;)"; + int result = 0; + uint8_t httpbuf1[] = + "POST / HTTP/1.1\r\n" + "Host: www.emergingthreats.net\r\n" + "User-Agent: Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.13) Gecko/2009080315 Ubuntu/8.10 (intrepid) Firefox/3.0.13\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8\r\n"; + uint8_t httpbuf2[] = + "Accept-Language: es-es,es;q=0.8,en-us;q=0.5,en;q=0.3\r\n" + "Accept-Encoding: gzip,deflate\r\n" + "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" + "Date: Tue, 22 Sep 2009 19:24:48 GMT\r\n" + "Server: Apache\r\n" + "Content-Length: 500\r\n" + "\r\n" + "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(); + if (de_ctx == NULL) { + goto end; + } + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, sig); + if (s == NULL) { + printf("sig parse failed: "); + goto end; + } + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + int r = AppLayerParse(NULL, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + goto end; + } + HtpState *http_state = f.alstate; + if (http_state == NULL) { + printf("no http state: "); + goto end; + } + + /* do detect for p1 */ + SCLogInfo("p1"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + + if (PacketAlertCheck(p1, 1)) { + printf("sid 1 matched on p1 but should not have: "); + goto end; + } + + r = AppLayerParse(NULL, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2); + if (r != 0) { + printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); + goto end; + } + /* do detect for p2 */ + SCLogInfo("p2"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + + if (!(PacketAlertCheck(p2, 1))) { + printf("sid 1 didn't match on p2 but should have: "); + goto end; + } + + FlowVar *fv = FlowVarGet(&f, 1); + if (fv == NULL) { + printf("no flowvar: "); + goto end; + } + + if (fv->data.fv_int.value != 2) { + printf("%u != %u: ", fv->data.fv_int.value, 2); + goto end; + } + + result = 1; +end: + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePackets(&p1, 1); + UTHFreePackets(&p2, 1); + return result; +} + +/** \test http buffer, flowints */ +static int LuajitMatchTest06(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; luajit:unittest; sid:1;)"; + int result = 0; + uint8_t httpbuf1[] = + "POST / HTTP/1.1\r\n" + "Host: www.emergingthreats.net\r\n" + "User-Agent: Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.13) Gecko/2009080315 Ubuntu/8.10 (intrepid) Firefox/3.0.13\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8\r\n"; + uint8_t httpbuf2[] = + "Accept-Language: es-es,es;q=0.8,en-us;q=0.5,en;q=0.3\r\n" + "Accept-Encoding: gzip,deflate\r\n" + "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" + "Date: Tue, 22 Sep 2009 19:24:48 GMT\r\n" + "Server: Apache\r\n" + "Content-Length: 500\r\n" + "\r\n" + "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(); + if (de_ctx == NULL) { + goto end; + } + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, sig); + if (s == NULL) { + printf("sig parse failed: "); + goto end; + } + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + int r = AppLayerParse(NULL, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + goto end; + } + HtpState *http_state = f.alstate; + if (http_state == NULL) { + printf("no http state: "); + goto end; + } + + /* do detect for p1 */ + SCLogInfo("p1"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + + if (PacketAlertCheck(p1, 1)) { + printf("sid 1 matched on p1 but should not have: "); + goto end; + } + + r = AppLayerParse(NULL, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2); + if (r != 0) { + printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); + goto end; + } + /* do detect for p2 */ + SCLogInfo("p2"); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + + if (!(PacketAlertCheck(p2, 1))) { + printf("sid 1 didn't match on p2 but should have: "); + goto end; + } + + FlowVar *fv = FlowVarGet(&f, 1); + if (fv == NULL) { + printf("no flowvar: "); + goto end; + } + + if (fv->data.fv_int.value != 0) { + printf("%u != %u: ", fv->data.fv_int.value, 0); + goto end; + } + + result = 1; +end: + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePackets(&p1, 1); + UTHFreePackets(&p2, 1); + return result; +} + #endif void DetectLuajitRegisterTests(void) { @@ -1483,6 +1776,8 @@ void DetectLuajitRegisterTests(void) { UtRegisterTest("LuajitMatchTest02", LuajitMatchTest02, 1); UtRegisterTest("LuajitMatchTest03", LuajitMatchTest03, 1); UtRegisterTest("LuajitMatchTest04", LuajitMatchTest04, 1); + UtRegisterTest("LuajitMatchTest05", LuajitMatchTest05, 1); + UtRegisterTest("LuajitMatchTest06", LuajitMatchTest06, 1); #endif }