--- /dev/null
+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
.. toctree::
base64
+ bytevar
config
dns
file
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)
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 \
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 \
#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)
{
*/
int LuaRegisterExtensions(lua_State *lua_state)
{
- lua_pushcfunction(lua_state, LuaGetByteVar);
- lua_setglobal(lua_state, "SCByteVarGet");
-
LuaRegisterFunctions(lua_state);
return 0;
}
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;
}
}
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);
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");
#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;
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;
#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"
static const luaL_Reg builtins[] = {
{ "suricata.base64", SCLuaLoadBase64Lib },
+ { "suricata.bytevar", LuaLoadBytevarLib },
{ "suricata.config", SCLuaLoadConfigLib },
{ "suricata.dataset", LuaLoadDatasetLib },
{ "suricata.dnp3", SCLuaLoadDnp3Lib },
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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 */