From 682392bafea8205d148defe095c3056de302905f Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Tue, 11 Feb 2025 15:51:25 -0600 Subject: [PATCH 1/4] output-lua: rename script directory variable It had a rather generic name of "path", which in Lua usually means a search path, which we'll be adding. --- src/output-lua.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/output-lua.c b/src/output-lua.c index 001404b97f0d..067d6dbd3079 100644 --- a/src/output-lua.c +++ b/src/output-lua.c @@ -58,7 +58,7 @@ * it's parent_ctx->data ptr. */ typedef struct LogLuaMasterCtx_ { - char path[PATH_MAX]; /**< contains script-dir */ + char script_dir[PATH_MAX]; /**< contains script-dir */ } LogLuaMasterCtx; typedef struct LogLuaCtx_ { @@ -625,7 +625,7 @@ static OutputInitResult OutputLuaLogInitSub(ConfNode *conf, OutputCtx *parent_ct const char *dir = ""; if (parent_ctx && parent_ctx->data) { LogLuaMasterCtx *mc = parent_ctx->data; - dir = mc->path; + dir = mc->script_dir; } char path[PATH_MAX] = ""; @@ -702,7 +702,7 @@ static OutputInitResult OutputLuaLogInit(ConfNode *conf) return result; } LogLuaMasterCtx *master_config = output_ctx->data; - strlcpy(master_config->path, dir, sizeof(master_config->path)); + strlcpy(master_config->script_dir, dir, sizeof(master_config->script_dir)); TAILQ_INIT(&output_ctx->submodules); /* check the enables scripts and set them up as submodules */ From 9d7dcd020429fde7df7a553658ff7201b4018924 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Tue, 11 Feb 2025 15:52:29 -0600 Subject: [PATCH 2/4] output-lua: remove unused includes --- src/output-lua.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/output-lua.c b/src/output-lua.c index 067d6dbd3079..c3877ead14f3 100644 --- a/src/output-lua.c +++ b/src/output-lua.c @@ -26,19 +26,12 @@ #include "output-lua.h" #include "util-lua-builtins.h" -#include "util-print.h" -#include "util-unittest.h" #include "util-debug.h" #include "output.h" #include "app-layer-htp.h" -#include "app-layer.h" #include "app-layer-ssl.h" #include "app-layer-ssh.h" #include "app-layer-parser.h" -#include "util-privs.h" -#include "util-buffer.h" -#include "util-proto-name.h" -#include "util-logopenfile.h" #include "util-time.h" #include "util-lua.h" #include "util-lua-common.h" From 8806dca1cf126eb5d885a5c65cf6c15baa513bdf Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Tue, 11 Feb 2025 16:26:39 -0600 Subject: [PATCH 3/4] output-lua: lua module search path configuration By default, use an empty search path. This gives us a predictable default. If a user needs access to external modules, the search path must be set in the configuration file. Ticket: #7169 --- src/output-lua.c | 67 ++++++++++++++++++++++++++++++++++++++++-------- suricata.yaml.in | 9 +++++++ 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/output-lua.c b/src/output-lua.c index c3877ead14f3..f2461eb24321 100644 --- a/src/output-lua.c +++ b/src/output-lua.c @@ -51,7 +51,14 @@ * it's parent_ctx->data ptr. */ typedef struct LogLuaMasterCtx_ { - char script_dir[PATH_MAX]; /**< contains script-dir */ + /** \brief Path to script directory. */ + char script_dir[PATH_MAX]; + + /** \brief Lua search path for Lua modules. */ + char path[PATH_MAX]; + + /** \brief Lua search path for C modules. */ + char cpath[PATH_MAX]; } LogLuaMasterCtx; typedef struct LogLuaCtx_ { @@ -396,6 +403,33 @@ typedef struct LogLuaScriptOptions_ { int stats; } LogLuaScriptOptions; +/** \brief Setup or clear Lua module search paths. + * + * If search paths are provided by the configuration, set them up, + * otherwise clear the default search paths. + */ +static void LuaSetPaths(lua_State *L, LogLuaMasterCtx *ctx) +{ + lua_getglobal(L, "package"); + + if (strlen(ctx->path) > 0) { + lua_pushstring(L, ctx->path); + } else { + lua_pushstring(L, ""); + } + lua_setfield(L, -2, "path"); + + if (strlen(ctx->cpath) > 0) { + lua_pushstring(L, ctx->cpath); + } else { + lua_pushstring(L, ""); + } + lua_setfield(L, -2, "cpath"); + + /* Pop package. */ + lua_pop(L, 1); +} + /** \brief load and evaluate the script * * This function parses the script, checks if all the required functions @@ -406,12 +440,14 @@ typedef struct LogLuaScriptOptions_ { * \param options struct to pass script requirements/options back to caller * \retval errcode 0 ok, -1 error */ -static int LuaScriptInit(const char *filename, LogLuaScriptOptions *options) { +static int LuaScriptInit(const char *filename, LogLuaScriptOptions *options, LogLuaMasterCtx *ctx) +{ lua_State *luastate = LuaGetState(); if (luastate == NULL) goto error; luaL_openlibs(luastate); SCLuaRequirefBuiltIns(luastate); + LuaSetPaths(luastate, ctx); int status = luaL_loadfile(luastate, filename); if (status) { @@ -537,7 +573,7 @@ static int LuaScriptInit(const char *filename, LogLuaScriptOptions *options) { * * \retval state Returns the set up luastate on success, NULL on error */ -static lua_State *LuaScriptSetup(const char *filename) +static lua_State *LuaScriptSetup(const char *filename, LogLuaMasterCtx *ctx) { lua_State *luastate = LuaGetState(); if (luastate == NULL) { @@ -547,6 +583,7 @@ static lua_State *LuaScriptSetup(const char *filename) luaL_openlibs(luastate); SCLuaRequirefBuiltIns(luastate); + LuaSetPaths(luastate, ctx); int status = luaL_loadfile(luastate, filename); if (status) { @@ -615,12 +652,11 @@ static OutputInitResult OutputLuaLogInitSub(ConfNode *conf, OutputCtx *parent_ct SCMutexInit(&lua_ctx->m, NULL); - const char *dir = ""; - if (parent_ctx && parent_ctx->data) { - LogLuaMasterCtx *mc = parent_ctx->data; - dir = mc->script_dir; - } + BUG_ON(parent_ctx == NULL); + LogLuaMasterCtx *mc = parent_ctx->data; + BUG_ON(mc == NULL); + const char *dir = mc->script_dir; char path[PATH_MAX] = ""; int ret = snprintf(path, sizeof(path),"%s%s%s", dir, strlen(dir) ? "/" : "", conf->val); if (ret < 0 || ret == sizeof(path)) { @@ -630,7 +666,7 @@ static OutputInitResult OutputLuaLogInitSub(ConfNode *conf, OutputCtx *parent_ct SCLogDebug("script full path %s", path); SCMutexLock(&lua_ctx->m); - lua_ctx->luastate = LuaScriptSetup(path); + lua_ctx->luastate = LuaScriptSetup(path, mc); SCMutexUnlock(&lua_ctx->m); if (lua_ctx->luastate == NULL) goto error; @@ -696,6 +732,17 @@ static OutputInitResult OutputLuaLogInit(ConfNode *conf) } LogLuaMasterCtx *master_config = output_ctx->data; strlcpy(master_config->script_dir, dir, sizeof(master_config->script_dir)); + + const char *lua_path = ConfNodeLookupChildValue(conf, "path"); + if (lua_path && strlen(lua_path) > 0) { + strlcpy(master_config->path, lua_path, sizeof(master_config->path)); + } + + const char *lua_cpath = ConfNodeLookupChildValue(conf, "cpath"); + if (lua_cpath && strlen(lua_cpath) > 0) { + strlcpy(master_config->cpath, lua_cpath, sizeof(master_config->cpath)); + } + TAILQ_INIT(&output_ctx->submodules); /* check the enables scripts and set them up as submodules */ @@ -709,7 +756,7 @@ static OutputInitResult OutputLuaLogInit(ConfNode *conf) snprintf(path, sizeof(path),"%s%s%s", dir, strlen(dir) ? "/" : "", script->val); SCLogDebug("script full path %s", path); - int r = LuaScriptInit(path, &opts); + int r = LuaScriptInit(path, &opts, master_config); if (r != 0) { SCLogError("couldn't initialize script"); goto error; diff --git a/suricata.yaml.in b/suricata.yaml.in index 46c94f0defbb..336c42c081f6 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -555,6 +555,15 @@ outputs: # https://docs.suricata.io/en/latest/output/lua-output.html - lua: enabled: no + + # By default the Lua module search paths are empty. If you plan + # to use external modules these paths will need to be set. The + # examples below are likely suitable for finding modules + # installed with a package manager on a 64 bit Linux system, but + # may need tweaking. + #path: "/usr/share/lua/5.4/?.lua;/usr/share/lua/5.4/?/init.lua;/usr/lib64/lua/5.4/?.lua;/usr/lib64/lua/5.4/?/init.lua;./?.lua;./?/init.lua" + #cpath: "/usr/lib64/lua/5.4/?.so;/usr/lib64/lua/5.4/loadall.so;./?.so" + #scripts-dir: /etc/suricata/lua-output/ scripts: # - script1.lua From 1f8fab76556ea8b9a73ca66cdfd76c85032d25e7 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Tue, 11 Feb 2025 16:37:54 -0600 Subject: [PATCH 4/4] doc/userguide: upgrade notes for Lua - Sandboxed Lua for rules - Search path changes for Lua output scripts --- doc/userguide/output/lua-output.rst | 11 +++++++++++ doc/userguide/upgrade.rst | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/doc/userguide/output/lua-output.rst b/doc/userguide/output/lua-output.rst index ab520e1aaa24..9401d94e5e21 100644 --- a/doc/userguide/output/lua-output.rst +++ b/doc/userguide/output/lua-output.rst @@ -75,6 +75,8 @@ Example: file:close(file) end +.. _lua-output-yaml: + YAML ---- @@ -87,6 +89,15 @@ scripts like so: - lua: enabled: yes scripts-dir: /etc/suricata/lua-output/ + + # By default the Lua module search paths are empty. If you plan + # to use external modules these paths will need to be set. The + # examples below are likely suitable for finding modules + # installed with a package manager on a 64 bit Linux system, but + # may need tweaking. + #path: "/usr/share/lua/5.4/?.lua;/usr/share/lua/5.4/?/init.lua;/usr/lib64/lua/5.4/?.lua;/usr/lib64/lua/5.4/?/init.lua;./?.lua;./?/init.lua" + #cpath: "/usr/lib64/lua/5.4/?.so;/usr/lib64/lua/5.4/loadall.so;./?.so" + scripts: - tcp-data.lua - flow.lua diff --git a/doc/userguide/upgrade.rst b/doc/userguide/upgrade.rst index f5df98100fba..282c5a0cf1de 100644 --- a/doc/userguide/upgrade.rst +++ b/doc/userguide/upgrade.rst @@ -89,6 +89,12 @@ Major changes Suricata 8.0, ``stream.checksum-validation`` no longer affects the checksum rule keywords. E.g., ``ipv4-csum: valid`` will only match if the check sum is valid, even when engine checksum validations are disabled. +- Lua detection scripts (rules) now run in a sandboxed + environment. See :ref:`lua-detection`. +- Lua output scripts have no default module search path, a search path + will need to be set before external modules can be loaded. See the + new default configuration file or :ref:`lua-output-yaml` for more + details. Removals ~~~~~~~~