You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/detect-lua.c

2023 lines
60 KiB
C

/* Copyright (C) 2007-2014 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author Victor Julien <victor@inliniac.net>
*
*/
#include "suricata-common.h"
#include "conf.h"
#include "threads.h"
#include "debug.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 "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-unittest.h"
#include "util-unittest-helper.h"
#include "app-layer.h"
#include "app-layer-parser.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, char *c)
{
SCLogError(SC_ERR_NO_LUA_SUPPORT, "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].Setup = DetectLuaSetupNoSupport;
sigmatch_table[DETECT_LUA].Free = NULL;
sigmatch_table[DETECT_LUA].RegisterTests = 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 (ThreadVars *, DetectEngineThreadCtx *,
Packet *, const Signature *, const SigMatchCtx *);
static int DetectLuaAppTxMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags,
void *state, void *txv, const Signature *s,
const SigMatchCtx *ctx);
static int DetectLuaSetup (DetectEngineCtx *, Signature *, char *);
static void DetectLuaRegisterTests(void);
static void DetectLuaFree(void *);
static int g_smtp_generic_list_id = 0;
static int InspectSmtpGeneric(ThreadVars *tv,
DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
const Signature *s, const SigMatchData *smd,
Flow *f, uint8_t flags, void *alstate,
void *txv, uint64_t tx_id);
/**
* \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 = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Lua_scripting";
sigmatch_table[DETECT_LUA].Match = DetectLuaMatch;
sigmatch_table[DETECT_LUA].AppLayerTxMatch = DetectLuaAppTxMatch;
sigmatch_table[DETECT_LUA].Setup = DetectLuaSetup;
sigmatch_table[DETECT_LUA].Free = DetectLuaFree;
sigmatch_table[DETECT_LUA].RegisterTests = DetectLuaRegisterTests;
g_smtp_generic_list_id = DetectBufferTypeRegister("smtp_generic");
DetectAppLayerInspectEngineRegister("smtp_generic",
ALPROTO_SMTP, SIG_FLAG_TOSERVER,
InspectSmtpGeneric);
DetectAppLayerInspectEngineRegister("smtp_generic",
ALPROTO_SMTP, SIG_FLAG_TOCLIENT,
InspectSmtpGeneric);
SCLogDebug("registering lua rule option");
return;
}
static int InspectSmtpGeneric(ThreadVars *tv,
DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
const Signature *s, const SigMatchData *smd,
Flow *f, uint8_t flags, void *alstate,
void *txv, uint64_t tx_id)
{
return DetectEngineInspectGenericList(tv, de_ctx, det_ctx, s, smd,
f, flags, alstate, txv, tx_id);
}
#define DATATYPE_PACKET (1<<0)
#define DATATYPE_PAYLOAD (1<<1)
#define DATATYPE_STREAM (1<<2)
#define DATATYPE_HTTP_URI (1<<3)
#define DATATYPE_HTTP_URI_RAW (1<<4)
#define DATATYPE_HTTP_REQUEST_HEADERS (1<<5)
#define DATATYPE_HTTP_REQUEST_HEADERS_RAW (1<<6)
#define DATATYPE_HTTP_REQUEST_COOKIE (1<<7)
#define DATATYPE_HTTP_REQUEST_UA (1<<8)
#define DATATYPE_HTTP_REQUEST_LINE (1<<9)
#define DATATYPE_HTTP_REQUEST_BODY (1<<10)
#define DATATYPE_HTTP_RESPONSE_COOKIE (1<<11)
#define DATATYPE_HTTP_RESPONSE_BODY (1<<12)
#define DATATYPE_HTTP_RESPONSE_HEADERS (1<<13)
#define DATATYPE_HTTP_RESPONSE_HEADERS_RAW (1<<14)
#define DATATYPE_DNS_RRNAME (1<<15)
#define DATATYPE_DNS_REQUEST (1<<16)
#define DATATYPE_DNS_RESPONSE (1<<17)
#define DATATYPE_TLS (1<<18)
#define DATATYPE_SSH (1<<19)
#define DATATYPE_SMTP (1<<20)
#define DATATYPE_DNP3 (1<<20)
/** \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");
}
}
int DetectLuaMatchBuffer(DetectEngineThreadCtx *det_ctx,
const Signature *s, const SigMatchData *smd,
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, 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) {
if (atoi(v) == 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 (ThreadVars *tv, 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, tv);
LuaExtensionsMatchSetup(tlua->luastate, lua, det_ctx,
p->flow, p, 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_HTTP) {
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(IPPROTO_TCP, ALPROTO_HTTP, htp_state);
for ( ; idx < total_txs; idx++) {
tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, 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) {
if (atoi(v) == 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 (ThreadVars *t, 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, 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_HTTP) {
HtpState *htp_state = state;
if (htp_state != NULL && htp_state->connp != NULL) {
htp_tx_t *tx = NULL;
tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, 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) {
if (atoi(v) == 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 (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags,
void *state, void *txv, const Signature *s,
const SigMatchCtx *ctx)
{
return DetectLuaAppMatchCommon(t, 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(SC_ERR_LUA_ERROR, "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(SC_ERR_LUA_ERROR, "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(SC_ERR_LUA_ERROR, "couldn't load file: %s", lua_tostring(t->luastate, -1));
goto error;
}
} else {
#endif
status = luaL_loadfile(t->luastate, lua->filename);
if (status) {
SCLogError(SC_ERR_LUA_ERROR, "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(SC_ERR_LUA_ERROR, "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 str Pointer to the user provided option
*
* \retval lua pointer to DetectLuaData on success
* \retval NULL on failure
*/
static DetectLuaData *DetectLuaParse (const DetectEngineCtx *de_ctx, 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(lua);
return NULL;
}
static int DetectLuaSetupPrime(DetectEngineCtx *de_ctx, DetectLuaData *ld)
{
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(SC_ERR_LUA_ERROR, "couldn't load file: %s", lua_tostring(luastate, -1));
goto error;
}
} else {
#endif
status = luaL_loadfile(luastate, ld->filename);
if (status) {
SCLogError(SC_ERR_LUA_ERROR, "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(SC_ERR_LUA_ERROR, "couldn't prime file: %s", lua_tostring(luastate, -1));
goto error;
}
lua_getglobal(luastate, "init");
if (lua_type(luastate, -1) != LUA_TFUNCTION) {
SCLogError(SC_ERR_LUA_ERROR, "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(SC_ERR_LUA_ERROR, "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(SC_ERR_LUA_ERROR, "couldn't run script 'init' function: %s", lua_tostring(luastate, -1));
goto error;
}
/* process returns from script */
if (lua_gettop(luastate) == 0) {
SCLogError(SC_ERR_LUA_ERROR, "init function in script should return table, nothing returned");
goto error;
}
if (lua_type(luastate, 1) != LUA_TTABLE) {
SCLogError(SC_ERR_LUA_ERROR, "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 separately as it has 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(SC_ERR_LUA_ERROR, "too many flowvars registered");
goto error;
}
uint32_t idx = VarNameStoreSetupAdd((char *)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(SC_ERR_LUA_ERROR, "too many flowints registered");
goto error;
}
uint32_t idx = VarNameStoreSetupAdd((char *)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;
}
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, "stream") == 0 && strcmp(v, "true") == 0) {
ld->flags |= DATATYPE_STREAM;
ld->buffername = SCStrdup("stream");
if (ld->buffername == NULL) {
SCLogError(SC_ERR_LUA_ERROR, "alloc error");
goto error;
}
} else if (strncmp(k, "http", 4) == 0 && strcmp(v, "true") == 0) {
if (ld->alproto != ALPROTO_UNKNOWN && ld->alproto != ALPROTO_HTTP) {
SCLogError(SC_ERR_LUA_ERROR, "can just inspect script against one app layer proto like HTTP at a time");
goto error;
}
if (ld->flags != 0) {
SCLogError(SC_ERR_LUA_ERROR, "when inspecting HTTP buffers only a single buffer can be inspected");
goto error;
}
/* http types */
ld->alproto = ALPROTO_HTTP;
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(SC_ERR_LUA_ERROR, "unsupported http data type %s", k);
goto error;
}
ld->buffername = SCStrdup(k);
if (ld->buffername == NULL) {
SCLogError(SC_ERR_LUA_ERROR, "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(SC_ERR_LUA_ERROR, "unsupported dns data type %s", k);
goto error;
}
ld->buffername = SCStrdup(k);
if (ld->buffername == NULL) {
SCLogError(SC_ERR_LUA_ERROR, "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(SC_ERR_LUA_ERROR, "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, char *str)
{
DetectLuaData *lua = NULL;
SigMatch *sm = NULL;
lua = DetectLuaParse(de_ctx, str);
if (lua == NULL)
goto error;
if (DetectLuaSetupPrime(de_ctx, lua) == -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 && lua->alproto != s->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;
if (lua->alproto == ALPROTO_UNKNOWN) {
if (lua->flags & DATATYPE_STREAM)
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_PMATCH);
else
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
} else if (lua->alproto == ALPROTO_HTTP) {
if (lua->flags & DATATYPE_HTTP_RESPONSE_BODY) {
int list = DetectBufferTypeGetByName("file_data");
SigMatchAppendSMToList(s, sm, list);
} else if (lua->flags & DATATYPE_HTTP_REQUEST_BODY) {
int list = DetectBufferTypeGetByName("http_client_body");
SigMatchAppendSMToList(s, sm, list);
} else if (lua->flags & DATATYPE_HTTP_URI) {
int list = DetectBufferTypeGetByName("http_uri");
SigMatchAppendSMToList(s, sm, list);
} else if (lua->flags & DATATYPE_HTTP_URI_RAW) {
int list = DetectBufferTypeGetByName("http_raw_uri");
SigMatchAppendSMToList(s, sm, list);
} else if (lua->flags & DATATYPE_HTTP_REQUEST_COOKIE ||
lua->flags & DATATYPE_HTTP_RESPONSE_COOKIE)
{
int list = DetectBufferTypeGetByName("http_cookie");
SigMatchAppendSMToList(s, sm, list);
} else if (lua->flags & DATATYPE_HTTP_REQUEST_UA) {
int list = DetectBufferTypeGetByName("http_user_agent");
SigMatchAppendSMToList(s, sm, list);
} else if (lua->flags & (DATATYPE_HTTP_REQUEST_HEADERS|DATATYPE_HTTP_RESPONSE_HEADERS)) {
int list = DetectBufferTypeGetByName("http_header");
SigMatchAppendSMToList(s, sm, list);
} else if (lua->flags & (DATATYPE_HTTP_REQUEST_HEADERS_RAW|DATATYPE_HTTP_RESPONSE_HEADERS_RAW)) {
int list = DetectBufferTypeGetByName("http_raw_header");
SigMatchAppendSMToList(s, sm, list);
} else {
int list = DetectBufferTypeGetByName("http_request_line");
SigMatchAppendSMToList(s, sm, list);
}
} else if (lua->alproto == ALPROTO_DNS) {
if (lua->flags & DATATYPE_DNS_RRNAME) {
int list = DetectBufferTypeGetByName("dns_query");
SigMatchAppendSMToList(s, sm, list);
} else if (lua->flags & DATATYPE_DNS_REQUEST) {
int list = DetectBufferTypeGetByName("dns_request");
SigMatchAppendSMToList(s, sm, list);
} else if (lua->flags & DATATYPE_DNS_RESPONSE) {
int list = DetectBufferTypeGetByName("dns_response");
SigMatchAppendSMToList(s, sm, list);
}
} else if (lua->alproto == ALPROTO_TLS) {
int list = DetectBufferTypeGetByName("tls_generic");
SigMatchAppendSMToList(s, sm, list);
} else if (lua->alproto == ALPROTO_SSH) {
int list = DetectBufferTypeGetByName("ssh_banner");
SigMatchAppendSMToList(s, sm, list);
} else if (lua->alproto == ALPROTO_SMTP) {
SigMatchAppendSMToList(s, sm, g_smtp_generic_list_id);
} else if (lua->alproto == ALPROTO_DNP3) {
int list = DetectBufferTypeGetByName("dnp3");
SigMatchAppendSMToList(s, sm, list);
} else {
SCLogError(SC_ERR_LUA_ERROR, "lua can't be used with protocol %s",
AppLayerGetProtoName(lua->alproto));
goto error;
}
return 0;
error:
if (lua != NULL)
DetectLuaFree(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(void *ptr)
{
if (ptr != NULL) {
DetectLuaData *lua = (DetectLuaData *)ptr;
if (lua->buffername)
SCFree(lua->buffername);
if (lua->filename)
SCFree(lua->filename);
SCFree(lua);
}
}
#ifdef UNITTESTS
/** \test http buffer */
static int LuaMatchTest01(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;)";
int result = 0;
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;
Packet *p1 = NULL;
Packet *p2 = NULL;
Flow f;
Signature *s = NULL;
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));
p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
FLOW_INITIALIZE(&f);
f.protoctx = (void *)&ssn;
f.proto = IPPROTO_TCP;
f.flags |= FLOW_IPV4;
f.alproto = ALPROTO_HTTP;
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();
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);
FLOWLOCK_WRLOCK(&f);
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP,
STREAM_TOSERVER, httpbuf1, httplen1);
if (r != 0) {
printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
FLOWLOCK_UNLOCK(&f);
goto end;
}
FLOWLOCK_UNLOCK(&f);
HtpState *http_state = f.alstate;
if (http_state == NULL) {
printf("no http state: ");
goto end;
}
/* do detect for p1 */
SCLogDebug("inspecting p1");
SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
if ((PacketAlertCheck(p1, 1))) {
printf("sid 1 didn't match on p1 but should have: ");
goto end;
}
FLOWLOCK_WRLOCK(&f);
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP,
STREAM_TOSERVER, httpbuf2, httplen2);
if (r != 0) {
printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
FLOWLOCK_UNLOCK(&f);
goto end;
}
FLOWLOCK_UNLOCK(&f);
/* do detect for p2 */
SCLogDebug("inspecting 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_str.value_len != 1) {
printf("%u != %u: ", fv->data.fv_str.value_len, 1);
goto end;
}
if (memcmp(fv->data.fv_str.value, "2", 1) != 0) {
PrintRawDataFp(stdout, fv->data.fv_str.value, fv->data.fv_str.value_len);
printf("buffer mismatch: ");
goto end;
}
result = 1;
end:
if (alp_tctx != NULL)
AppLayerParserThreadCtxFree(alp_tctx);
if (de_ctx != NULL)
DetectEngineCtxFree(de_ctx);
StreamTcpFreeConfig(TRUE);
FLOW_DESTROY(&f);
UTHFreePackets(&p1, 1);
UTHFreePackets(&p2, 1);
return result;
}
/** \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;)";
int result = 0;
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;
Packet *p1 = NULL;
Packet *p2 = NULL;
Flow f;
Signature *s = NULL;
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));
p1 = UTHBuildPacket(httpbuf1, httplen1, IPPROTO_TCP);
p2 = UTHBuildPacket(httpbuf2, httplen2, IPPROTO_TCP);
FLOW_INITIALIZE(&f);
f.protoctx = (void *)&ssn;
f.proto = IPPROTO_TCP;
f.flags |= FLOW_IPV4;
f.alproto = ALPROTO_HTTP;
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();
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);
/* do detect for p1 */
SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
if ((PacketAlertCheck(p1, 1))) {
printf("sid 1 didn't match on p1 but should have: ");
goto end;
}
/* do detect for 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_str.value_len != 1) {
printf("%u != %u: ", fv->data.fv_str.value_len, 1);
goto end;
}
if (memcmp(fv->data.fv_str.value, "2", 1) != 0) {
PrintRawDataFp(stdout, fv->data.fv_str.value, fv->data.fv_str.value_len);
printf("buffer mismatch: ");
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 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;)";
int result = 0;
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;
Packet *p1 = NULL;
Packet *p2 = NULL;
Flow f;
Signature *s = NULL;
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));
p1 = UTHBuildPacket(httpbuf1, httplen1, IPPROTO_TCP);
p2 = UTHBuildPacket(httpbuf2, httplen2, IPPROTO_TCP);
FLOW_INITIALIZE(&f);
f.protoctx = (void *)&ssn;
f.proto = IPPROTO_TCP;
f.flags |= FLOW_IPV4;
f.alproto = ALPROTO_HTTP;
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();
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);
/* do detect for p1 */
SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
if ((PacketAlertCheck(p1, 1))) {
printf("sid 1 didn't match on p1 but should have: ");
goto end;
}
/* do detect for 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_str.value_len != 1) {
printf("%u != %u: ", fv->data.fv_str.value_len, 1);
goto end;
}
if (memcmp(fv->data.fv_str.value, "2", 1) != 0) {
PrintRawDataFp(stdout, fv->data.fv_str.value, fv->data.fv_str.value_len);
printf("buffer mismatch: ");
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 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;)";
int result = 0;
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;
Packet *p1 = NULL;
Packet *p2 = NULL;
Flow f;
Signature *s = NULL;
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));
p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
FLOW_INITIALIZE(&f);
f.protoctx = (void *)&ssn;
f.proto = IPPROTO_TCP;
f.flags |= FLOW_IPV4;
f.alproto = ALPROTO_HTTP;
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();
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);
FLOWLOCK_WRLOCK(&f);
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP,
STREAM_TOSERVER, httpbuf1, httplen1);
if (r != 0) {
printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
FLOWLOCK_UNLOCK(&f);
goto end;
}
FLOWLOCK_UNLOCK(&f);
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;
}
FLOWLOCK_WRLOCK(&f);
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP,
STREAM_TOSERVER, httpbuf2, httplen2);
if (r != 0) {
printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
FLOWLOCK_UNLOCK(&f);
goto end;
}
FLOWLOCK_UNLOCK(&f);
/* 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 (alp_tctx != NULL)
AppLayerParserThreadCtxFree(alp_tctx);
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 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;)";
int result = 0;
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;
Packet *p1 = NULL;
Packet *p2 = NULL;
Flow f;
Signature *s = NULL;
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));
p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
FLOW_INITIALIZE(&f);
f.protoctx = (void *)&ssn;
f.proto = IPPROTO_TCP;
f.flags |= FLOW_IPV4;
f.alproto = ALPROTO_HTTP;
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();
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);
FLOWLOCK_WRLOCK(&f);
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP,
STREAM_TOSERVER, httpbuf1, httplen1);
if (r != 0) {
printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
FLOWLOCK_UNLOCK(&f);
goto end;
}
FLOWLOCK_UNLOCK(&f);
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;
}
FLOWLOCK_WRLOCK(&f);
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP,
STREAM_TOSERVER, httpbuf2, httplen2);
if (r != 0) {
printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
FLOWLOCK_UNLOCK(&f);
goto end;
}
FLOWLOCK_UNLOCK(&f);
/* 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 (alp_tctx != NULL)
AppLayerParserThreadCtxFree(alp_tctx);
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 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;)";
int result = 0;
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;
Packet *p1 = NULL;
Packet *p2 = NULL;
Flow f;
Signature *s = NULL;
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));
p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
FLOW_INITIALIZE(&f);
f.protoctx = (void *)&ssn;
f.proto = IPPROTO_TCP;
f.flags |= FLOW_IPV4;
f.alproto = ALPROTO_HTTP;
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();
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);
FLOWLOCK_WRLOCK(&f);
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP,
STREAM_TOSERVER, httpbuf1, httplen1);
if (r != 0) {
printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
FLOWLOCK_UNLOCK(&f);
goto end;
}
FLOWLOCK_UNLOCK(&f);
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;
}
FLOWLOCK_WRLOCK(&f);
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP,
STREAM_TOSERVER, httpbuf2, httplen2);
if (r != 0) {
printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
FLOWLOCK_UNLOCK(&f);
goto end;
}
FLOWLOCK_UNLOCK(&f);
/* 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 (alp_tctx != NULL)
AppLayerParserThreadCtxFree(alp_tctx);
if (de_ctx != NULL)
DetectEngineCtxFree(de_ctx);
StreamTcpFreeConfig(TRUE);
FLOW_DESTROY(&f);
UTHFreePackets(&p1, 1);
UTHFreePackets(&p2, 1);
return result;
}
#endif
void DetectLuaRegisterTests(void)
{
#ifdef UNITTESTS
UtRegisterTest("LuaMatchTest01", LuaMatchTest01);
UtRegisterTest("LuaMatchTest02", LuaMatchTest02);
UtRegisterTest("LuaMatchTest03", LuaMatchTest03);
UtRegisterTest("LuaMatchTest04", LuaMatchTest04);
UtRegisterTest("LuaMatchTest05", LuaMatchTest05);
UtRegisterTest("LuaMatchTest06", LuaMatchTest06);
#endif
}
#endif /* HAVE_LUAJIT */