lua/bytevar: convert SCByteVar to Lua lib

Similar to flowvars and flowints, but a byte var cannot be registered
from a Lua script, but it still needs to be setup. Instead provide an
"map" function that sets it up, or errors out if the byte var is
unknown.

This also required passing the signature into the Lua init method, as
the state of the Signature object and the time of loading the Lua
keyword is required.
pull/13361/head
Jason Ish 1 month ago committed by Victor Julien
parent 5a19c95a07
commit 11b3ebcb5f

@ -0,0 +1,80 @@
Bytevar
#######
The ``suricata.bytevar`` module provides access to variables defined by
``byte_extract`` and ``byte_math`` keywords in Suricata rules.
It is only available in Suricata Lua rules, not output scripts.
Setup
*****
::
local bytevars = require("suricata.bytevar")
Module Functions
****************
.. function:: bytevars.map(sig, varname)
Ensures that the ``bytevar`` exists and sets it up for further use
in the script by mapping it into the Lua context. Must be called
during ``init()``.
:param sig: The signature object passed to ``init()``
:param string varname: Name of the variable as defined in the rule
:raises error: If the variable name is unknown
:raises error: If too many byte variables are mapped
Example:
::
function init(sig)
bytevars.map(sig, "var1")
bytevars.map(sig, "var2")
return {}
end
.. function:: bytevars.get(name)
Returns a byte variable object for the given name. May be called
during ``thread_init()`` to save a handle to the bytevar.
:param number name: Name of the variable previously setup with
``map()``.
:raises error: If variable name is not mapped with ``map()``.
:returns: A byte variable object
Example:
::
function thread_init()
bv_var1 = bytevars.get("var1")
bv_var2 = bytevars.get("var2")
end
Byte Variable Object Methods
****************************
.. method:: bytevar:value()
Returns the current value of the byte variable.
:returns: The value of the byte variable.
Example:
::
function match(args)
local var1 = bv_var1:value()
if var1 then
-- Use the value
end
end

@ -9,6 +9,7 @@ environment without access to additional modules.
.. toctree::
base64
bytevar
config
dns
file

@ -313,28 +313,3 @@ SCThreadInfo
tid, tname, tgroup = SCThreadInfo()
It gives: tid (integer), tname (string), tgroup (string)
SCByteVarGet
~~~~~~~~~~~~
Get the ByteVar at index given by the parameter. These variables are defined by
`byte_extract` or `byte_math` in Suricata rules. Only callable from match scripts.
::
function init(args)
local needs = {}
needs["bytevar"] = {"var1", "var2"}
return needs
end
Here we define a register that we will be using variables `var1` and `var2`.
The access to the Byte variables is done by index.
::
function match(args)
var1 = SCByteVarGet(0)
var2 = SCByteVarGet(1)

@ -545,6 +545,7 @@ noinst_HEADERS = \
util-lua-flowintlib.h \
util-lua-flowlib.h \
util-lua-flowvarlib.h \
util-lua-bytevarlib.h \
util-lua-hashlib.h \
util-lua-http.h \
util-lua-ja3.h \
@ -1124,6 +1125,7 @@ libsuricata_c_a_SOURCES = \
util-lua-flowintlib.c \
util-lua-flowlib.c \
util-lua-flowvarlib.c \
util-lua-bytevarlib.c \
util-lua-hashlib.c \
util-lua-http.c \
util-lua-ja3.c \

@ -38,54 +38,11 @@
#include "util-lua.h"
#include "util-lua-common.h"
#include "util-lua-smtp.h"
#include "util-lua-dnp3.h"
#include "detect-lua-extensions.h"
/* Lua registry key for DetectLuaData. */
const char luaext_key_ld[] = "suricata:luadata";
static int GetLuaData(lua_State *luastate, DetectLuaData **ret_ld)
{
*ret_ld = NULL;
DetectLuaData *ld;
lua_pushlightuserdata(luastate, (void *)&luaext_key_ld);
lua_gettable(luastate, LUA_REGISTRYINDEX);
ld = lua_touserdata(luastate, -1);
if (ld == NULL) {
LUA_ERROR("internal error: no ld");
}
*ret_ld = ld;
return 0;
}
static int LuaGetByteVar(lua_State *luastate)
{
DetectLuaData *ld = NULL;
DetectEngineThreadCtx *det_ctx = LuaStateGetDetCtx(luastate);
if (det_ctx == NULL)
return LuaCallbackError(luastate, "internal error: no ldet_ctx");
int ret = GetLuaData(luastate, &ld);
if (ret != 0)
return ret;
if (!lua_isnumber(luastate, 1)) {
LUA_ERROR("bytevar id not a number");
}
int id = lua_tonumber(luastate, 1);
if (id < 0 || id >= DETECT_LUA_MAX_BYTEVARS) {
LUA_ERROR("bytevar id out of range");
}
uint32_t idx = ld->bytevar[id];
lua_pushinteger(luastate, det_ctx->byte_values[idx]);
return 1;
}
void LuaExtensionsMatchSetup(lua_State *lua_state, DetectLuaData *ld,
DetectEngineThreadCtx *det_ctx, Flow *f, Packet *p, const Signature *s, uint8_t flags)
{
@ -115,9 +72,6 @@ void LuaExtensionsMatchSetup(lua_State *lua_state, DetectLuaData *ld,
*/
int LuaRegisterExtensions(lua_State *lua_state)
{
lua_pushcfunction(lua_state, LuaGetByteVar);
lua_setglobal(lua_state, "SCByteVarGet");
LuaRegisterFunctions(lua_state);
return 0;
}

@ -566,7 +566,11 @@ static int DetectLuaSetupPrime(DetectEngineCtx *de_ctx, DetectLuaData *ld, const
goto error;
}
if (lua_pcall(luastate, 0, 1, 0) != 0) {
/* Pass the signature as the first argument, setting up bytevars depends on
* access to the signature. */
lua_pushlightuserdata(luastate, (void *)s);
if (lua_pcall(luastate, 1, 1, 0) != 0) {
SCLogError("couldn't run script 'init' function: %s", lua_tostring(luastate, -1));
goto error;
}
@ -633,34 +637,6 @@ static int DetectLuaSetupPrime(DetectEngineCtx *de_ctx, DetectLuaData *ld, const
}
lua_pop(luastate, 1);
continue;
} else if (strcmp(k, "bytevar") == 0) {
if (lua_istable(luastate, -1)) {
lua_pushnil(luastate);
while (lua_next(luastate, -2) != 0) {
/* value at -1, key is at -2 which we ignore */
const char *value = lua_tostring(luastate, -1);
SCLogDebug("value %s", value);
/* removes 'value'; keeps 'key' for next iteration */
lua_pop(luastate, 1);
if (ld->bytevars == DETECT_LUA_MAX_BYTEVARS) {
SCLogError("too many bytevars registered");
goto error;
}
DetectByteIndexType idx;
if (!DetectByteRetrieveSMVar(value, s, &idx)) {
SCLogError("Unknown byte_extract or byte_math var "
"requested by lua script - %s",
value);
goto error;
}
ld->bytevar[ld->bytevars++] = idx;
SCLogDebug("script uses bytevar %u with script id %u", idx, ld->bytevars - 1);
}
}
lua_pop(luastate, 1);
continue;
}
bool required = lua_toboolean(luastate, -1);
@ -821,6 +797,9 @@ static void DetectLuaFree(DetectEngineCtx *de_ctx, void *ptr)
for (uint16_t i = 0; i < lua->flowvars; i++) {
VarNameStoreUnregister(lua->flowvar[i], VAR_TYPE_FLOW_VAR);
}
for (uint16_t i = 0; i < lua->bytevars; i++) {
SCFree(lua->bytevar[i].name);
}
DetectUnregisterThreadCtxFuncs(de_ctx, lua, "lua");

@ -36,6 +36,11 @@ typedef struct DetectLuaThreadData {
#define DETECT_LUA_MAX_FLOWINTS 15
#define DETECT_LUA_MAX_BYTEVARS 15
typedef struct DetectLuaDataBytevarEntry_ {
char *name;
uint32_t id;
} DetectLuaDataBytevarEntry;
typedef struct DetectLuaData {
int thread_ctx_id;
int negated;
@ -47,7 +52,7 @@ typedef struct DetectLuaData {
uint16_t flowvars;
uint32_t flowvar[DETECT_LUA_MAX_FLOWVARS];
uint16_t bytevars;
uint32_t bytevar[DETECT_LUA_MAX_BYTEVARS];
DetectLuaDataBytevarEntry bytevar[DETECT_LUA_MAX_BYTEVARS];
uint64_t alloc_limit;
uint64_t instruction_limit;
int allow_restricted_functions;

@ -18,6 +18,7 @@
#include "suricata-common.h"
#include "util-lua-builtins.h"
#include "util-lua-base64lib.h"
#include "util-lua-bytevarlib.h"
#include "util-lua-config.h"
#include "util-lua-dataset.h"
#include "util-lua-dnp3.h"
@ -40,6 +41,7 @@
static const luaL_Reg builtins[] = {
{ "suricata.base64", SCLuaLoadBase64Lib },
{ "suricata.bytevar", LuaLoadBytevarLib },
{ "suricata.config", SCLuaLoadConfigLib },
{ "suricata.dataset", LuaLoadDatasetLib },
{ "suricata.dnp3", SCLuaLoadDnp3Lib },

@ -0,0 +1,127 @@
/* Copyright (C) 2025 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.
*/
#include "suricata-common.h"
#include "detect-byte.h"
#include "util-lua-common.h"
#include "util-lua-bytevarlib.h"
#include "util-lua.h"
#include "detect-lua.h"
#include "detect-lua-extensions.h"
#include "lauxlib.h"
static const char suricata_bytevar_mt[] = "suricata:bytevar:mt";
static DetectLuaData *GetLuaData(lua_State *luastate)
{
DetectLuaData *ld;
lua_pushlightuserdata(luastate, (void *)&luaext_key_ld);
lua_gettable(luastate, LUA_REGISTRYINDEX);
ld = lua_touserdata(luastate, -1);
return ld;
}
static int LuaBytevarMap(lua_State *L)
{
const Signature *s = lua_touserdata(L, -2);
const char *name = luaL_checkstring(L, -1);
DetectLuaData *ld = GetLuaData(L);
/* Is this name already mapped? */
for (uint16_t i = 0; i < ld->bytevars; i++) {
if (strcmp(ld->bytevar[i].name, name) == 0) {
lua_pushinteger(L, ld->bytevar[i].id);
return 1;
}
}
if (ld->bytevars == DETECT_LUA_MAX_BYTEVARS) {
luaL_error(L, "too many bytevars mapped");
}
DetectByteIndexType idx;
if (!DetectByteRetrieveSMVar(name, s, &idx)) {
luaL_error(L, "unknown byte_extract or byte_math variable: %s", name);
}
ld->bytevar[ld->bytevars].name = SCStrdup(name);
if (ld->bytevar[ld->bytevars].name == NULL) {
luaL_error(L, "failed to allocate memory for bytevar name: %s", name);
}
ld->bytevar[ld->bytevars++].id = idx;
return 1;
}
static int LuaBytevarGet(lua_State *L)
{
const char *name = luaL_checkstring(L, 1);
DetectLuaData *ld = GetLuaData(L);
if (ld == NULL) {
return luaL_error(L, "internal error: no lua data");
}
for (uint16_t i = 0; i < ld->bytevars; i++) {
if (strcmp(ld->bytevar[i].name, name) == 0) {
uint32_t *bytevar_id = lua_newuserdata(L, sizeof(*bytevar_id));
*bytevar_id = ld->bytevar[i].id;
luaL_getmetatable(L, suricata_bytevar_mt);
lua_setmetatable(L, -2);
return 1;
}
}
return luaL_error(L, "unknown bytevar: %s", name);
}
static int LuaBytevarValue(lua_State *L)
{
uint32_t *bytevar_id = luaL_checkudata(L, 1, suricata_bytevar_mt);
DetectEngineThreadCtx *det_ctx = LuaStateGetDetCtx(L);
if (det_ctx == NULL) {
return LuaCallbackError(L, "internal error: no det_ctx");
}
lua_pushinteger(L, det_ctx->byte_values[*bytevar_id]);
return 1;
}
static const luaL_Reg bytevarlib[] = {
// clang-format off
{ "map", LuaBytevarMap, },
{ "get", LuaBytevarGet, },
{ NULL, NULL, },
// clang-format on
};
static const luaL_Reg bytevarmt[] = {
// clang-format off
{ "value", LuaBytevarValue, },
{ NULL, NULL, },
// clang-format on
};
int LuaLoadBytevarLib(lua_State *L)
{
luaL_newmetatable(L, suricata_bytevar_mt);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
luaL_setfuncs(L, bytevarmt, 0);
luaL_newlib(L, bytevarlib);
return 1;
}

@ -0,0 +1,25 @@
/* Copyright (C) 2025 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.
*/
#ifndef SURICATA_UTIL_LUA_BYTEVARLIB_H
#define SURICATA_UTIL_LUA_BYTEVARLIB_H
#include "lua.h"
int LuaLoadBytevarLib(lua_State *L);
#endif /* SURICATA_UTIL_LUA_BYTEVARLIB_H */
Loading…
Cancel
Save