|
|
|
/* 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 DetectLuaAppMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
|
|
|
|
Flow *f, uint8_t flags, void *state, const Signature *s, const SigMatchData *m);
|
|
|
|
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 *);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \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].AppLayerMatch = DetectLuaAppMatch;
|
|
|
|
sigmatch_table[DETECT_LUA].AppLayerTxMatch = DetectLuaAppTxMatch;
|
|
|
|
sigmatch_table[DETECT_LUA].Setup = DetectLuaSetup;
|
|
|
|
sigmatch_table[DETECT_LUA].Free = DetectLuaFree;
|
|
|
|
sigmatch_table[DETECT_LUA].RegisterTests = DetectLuaRegisterTests;
|
|
|
|
|
|
|
|
SCLogDebug("registering lua rule option");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#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;
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
12 years ago
|
|
|
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++) {
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
12 years ago
|
|
|
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 AMATCH
|
|
|
|
*
|
|
|
|
* \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 DetectLuaAppMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
|
|
|
|
Flow *f, uint8_t flags, void *state,
|
|
|
|
const Signature *s, const SigMatchData *m)
|
|
|
|
{
|
|
|
|
return DetectLuaAppMatchCommon(t, det_ctx, f, flags, state, s, m->ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \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;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t idx = VariableNameGetIdx(de_ctx, (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;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t idx = VariableNameGetIdx(de_ctx, (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)
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_FILEDATA);
|
|
|
|
else if (lua->flags & DATATYPE_HTTP_REQUEST_BODY)
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_HCBDMATCH);
|
|
|
|
else if (lua->flags & DATATYPE_HTTP_URI)
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_UMATCH);
|
|
|
|
else if (lua->flags & DATATYPE_HTTP_URI_RAW)
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_HRUDMATCH);
|
|
|
|
else if (lua->flags & DATATYPE_HTTP_REQUEST_COOKIE)
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_HCDMATCH);
|
|
|
|
else if (lua->flags & DATATYPE_HTTP_REQUEST_UA)
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_HUADMATCH);
|
|
|
|
else if (lua->flags & (DATATYPE_HTTP_REQUEST_HEADERS|DATATYPE_HTTP_RESPONSE_HEADERS))
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_HHDMATCH);
|
|
|
|
else if (lua->flags & (DATATYPE_HTTP_REQUEST_HEADERS_RAW|DATATYPE_HTTP_RESPONSE_HEADERS_RAW))
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_HRHDMATCH);
|
|
|
|
else if (lua->flags & DATATYPE_HTTP_RESPONSE_COOKIE)
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_HCDMATCH);
|
|
|
|
else {
|
|
|
|
int list = DetectBufferTypeGetByName("http_request_line");
|
|
|
|
SigMatchAppendSMToList(s, sm, list);
|
|
|
|
}
|
|
|
|
} else if (lua->alproto == ALPROTO_DNS) {
|
|
|
|
if (lua->flags & DATATYPE_DNS_RRNAME) {
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_DNSQUERYNAME_MATCH);
|
|
|
|
} else if (lua->flags & DATATYPE_DNS_REQUEST) {
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_DNSREQUEST_MATCH);
|
|
|
|
} else if (lua->flags & DATATYPE_DNS_RESPONSE) {
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_DNSRESPONSE_MATCH);
|
|
|
|
}
|
|
|
|
} else if (lua->alproto == ALPROTO_TLS) {
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
|
|
|
|
} else if (lua->alproto == ALPROTO_SSH) {
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
|
|
|
|
} else if (lua->alproto == ALPROTO_SMTP) {
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
|
|
|
|
} else if (lua->alproto == ALPROTO_DNP3) {
|
|
|
|
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_DNP3_MATCH);
|
|
|
|
} 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();
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
12 years ago
|
|
|
|
|
|
|
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;
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
12 years ago
|
|
|
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:
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
12 years ago
|
|
|
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;
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
12 years ago
|
|
|
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;
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
12 years ago
|
|
|
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();
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
12 years ago
|
|
|
|
|
|
|
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;
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
12 years ago
|
|
|
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:
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
12 years ago
|
|
|
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();
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
12 years ago
|
|
|
|
|
|
|
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;
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
12 years ago
|
|
|
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:
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
12 years ago
|
|
|
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();
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
12 years ago
|
|
|
|
|
|
|
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;
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
12 years ago
|
|
|
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:
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
12 years ago
|
|
|
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 */
|