]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
lua: add suricata.rule library
authorJason Ish <jason.ish@oisf.net>
Wed, 9 Apr 2025 21:19:29 +0000 (15:19 -0600)
committerVictor Julien <victor@inliniac.net>
Tue, 22 Apr 2025 20:43:05 +0000 (22:43 +0200)
Add a "suricata.rule" library for accessing rule information from a
Lua rule, or a Lua output script.

This lib replaces the following global Lua functions:
- SCRuleIds
- SCRuleAction
- SCRuleMsg
- SCRuleClass

Ticket: #7490

doc/userguide/lua/libs/index.rst
doc/userguide/lua/libs/rule.rst [new file with mode: 0644]
doc/userguide/lua/lua-functions.rst
src/Makefile.am
src/util-lua-builtins.c
src/util-lua-common.c
src/util-lua-rule.c [new file with mode: 0644]
src/util-lua-rule.h [new file with mode: 0644]

index 281cfac9adf47158873a2e960774f897131889fc..a0a87766eace55cff0f36d75d4b645bd2cec9b62 100644 (file)
@@ -14,3 +14,4 @@ environment without access to additional modules.
    hashlib
    http
    packetlib
+   rule
diff --git a/doc/userguide/lua/libs/rule.rst b/doc/userguide/lua/libs/rule.rst
new file mode 100644 (file)
index 0000000..5aa5130
--- /dev/null
@@ -0,0 +1,71 @@
+Rule
+====
+
+Rule details for an alert are exposed to Lua scripts with the
+``suricata.rule`` library, for example::
+
+  local rule = require("suricata.rule")
+
+Rule Setup
+----------
+
+For use in Suricata Lua rules, no additional setup is required.
+
+Output Setup
+------------
+
+For use in Suricata Lua output scripts, some additional setup is
+required::
+
+  function init(args)
+      return {
+          type = "packet",
+          filter = "alerts",
+      }
+  end
+
+Getting a Rule Instance
+-----------------------
+
+To obtain a rule object, use the ``get_rule()`` function on the
+``rule`` library::
+
+  local sig = rule.get_rule()
+
+Rule Methods
+------------
+
+``action()``
+^^^^^^^^^^^^
+
+Returns the action of the rule, for example: `alert`, `pass`.
+
+``class_description()``
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Returns the classification description.
+
+``gid()``
+^^^^^^^^^
+
+Returns the generator ID of the rule.
+
+``rev()``
+^^^^^^^^^
+
+Returns the revision of the rule.
+
+``msg()``
+^^^^^^^^^
+
+Returns the rule message (``msg``).
+
+``priority``
+^^^^^^^^^^^^
+
+Returns the priority of the rule as a number.
+
+``sid()``
+^^^^^^^^^
+
+Returns the signature ID of the rule.
index bcc0008bad923292edf61143bf34220c3719be9e..726864a02884b293885e7b36eb8f1bc36f9989b2 100644 (file)
@@ -644,51 +644,6 @@ SCFileState
 
 returns state (string), stored (bool)
 
-Alerts
-------
-
-Alerts are a subset of the 'packet' logger:
-
-::
-
-  function init (args)
-      local needs = {}
-      needs["type"] = "packet"
-      needs["filter"] = "alerts"
-      return needs
-  end
-
-SCRuleIds
-~~~~~~~~~
-
-::
-
-  sid, rev, gid = SCRuleIds()
-
-SCRuleAction
-~~~~~~~~~~~~
-
-::
-
-  action = SCRuleAction()
-
-returns one of 'pass', 'reject', 'drop' or 'alert'
-
-SCRuleMsg
-~~~~~~~~~
-
-::
-
-  msg = SCRuleMsg()
-
-SCRuleClass
-~~~~~~~~~~~
-
-::
-
-
-  class, prio = SCRuleClass()
-
 Streaming Data
 --------------
 
index ec42d4385413890a3d64db62fb4ef3e05ed4732f..ec169c585f3671afce53e17495d1eb5d0cad7634 100755 (executable)
@@ -531,6 +531,7 @@ noinst_HEADERS = \
        util-lua-http.h \
        util-lua-ja3.h \
        util-lua-packetlib.h \
+       util-lua-rule.h \
        util-lua-sandbox.h \
        util-lua-smtp.h \
        util-lua-ssh.h \
@@ -1096,6 +1097,7 @@ libsuricata_c_a_SOURCES = \
        util-lua-http.c \
        util-lua-ja3.c \
        util-lua-packetlib.c \
+       util-lua-rule.c \
        util-lua-sandbox.c \
        util-lua-smtp.c \
        util-lua-ssh.c \
index 84eeb0178867611778f4c7a0b0f16403c543b214..7ad5f0e2cc5cca9e0bcbb29a45616dd91cc09e61 100644 (file)
@@ -25,6 +25,7 @@
 #include "util-lua-flowlib.h"
 #include "util-lua-hashlib.h"
 #include "util-lua-packetlib.h"
+#include "util-lua-rule.h"
 
 #include "lauxlib.h"
 
@@ -37,6 +38,7 @@ static const luaL_Reg builtins[] = {
     { "suricata.hashlib", SCLuaLoadHashlib },
     { "suricata.http", SCLuaLoadHttpLib },
     { "suricata.packet", LuaLoadPacketLib },
+    { "suricata.rule", SCLuaLoadRuleLib },
     { NULL, NULL },
 };
 
index 921ddf315d2aa05dfd6d31af1dbfc030a4a0d7a9..944b384d6d9bbc17ea2aa07718881cd9cb3b2336 100644 (file)
@@ -133,160 +133,6 @@ static int LuaCallbackStreamingBuffer(lua_State *luastate)
     return LuaCallbackStreamingBufferPushToStack(luastate, b);
 }
 
-/** \internal
- *  \brief fill lua stack with signature info
- *  \param luastate the lua state
- *  \param s pointer to signature struct
- *  \retval cnt number of data items placed on the stack
- *
- *  Places: sid (number), rev (number), gid (number)
- */
-static int LuaCallbackRuleIdsPushToStackFromSignature(lua_State *luastate, const Signature *s)
-{
-    lua_pushinteger(luastate, s->id);
-    lua_pushinteger(luastate, s->rev);
-    lua_pushinteger(luastate, s->gid);
-    return 3;
-}
-
-/** \internal
- *  \brief Wrapper for getting tuple info into a lua script
- *  \retval cnt number of items placed on the stack
- *
- *  Info is pulled from PacketAlert if it exists in lua registry (true for logging scripts)
- *  otherwise pulled from Signature in lua registry (for match scripts)
- */
-static int LuaCallbackRuleIds(lua_State *luastate)
-{
-    const Signature *s = NULL;
-    const PacketAlert *pa = LuaStateGetPacketAlert(luastate);
-    if (pa != NULL) {
-        s = pa->s;
-    } else {
-        s = LuaStateGetSignature(luastate);
-        if (s == NULL)
-            return LuaCallbackError(luastate, "internal error: no packet alert or signature");
-    }
-    return LuaCallbackRuleIdsPushToStackFromSignature(luastate, s);
-}
-
-/** \internal
- *  \brief fill lua stack with signature info
- *  \param luastate the lua state
- *  \param s pointer to signature struct
- *  \retval cnt number of data items placed on the stack
- *
- *  Places: action (string)
- */
-static int LuaCallbackRuleActionPushToStackFromSignature(lua_State *luastate, const Signature *s)
-{
-    const char *action = "";
-    if (s->action & ACTION_PASS) {
-        action = "pass";
-    } else if ((s->action & ACTION_REJECT) || (s->action & ACTION_REJECT_BOTH) ||
-               (s->action & ACTION_REJECT_DST)) {
-        action = "reject";
-    } else if (s->action & ACTION_DROP) {
-        action = "drop";
-    } else if (s->action & ACTION_ALERT) {
-        action = "alert";
-    }
-    lua_pushstring(luastate, action);
-    return 1;
-}
-
-/** \internal
- *  \brief Wrapper for getting tuple info into a lua script
- *  \retval cnt number of items placed on the stack
- *
- *  Info is pulled from PacketAlert if it exists in lua registry (true for logging scripts)
- *  otherwise pulled from Signature in lua registry (for match scripts)
- */
-static int LuaCallbackRuleAction(lua_State *luastate)
-{
-    const Signature *s = NULL;
-    const PacketAlert *pa = LuaStateGetPacketAlert(luastate);
-    if (pa != NULL) {
-        s = pa->s;
-    } else {
-        s = LuaStateGetSignature(luastate);
-        if (s == NULL)
-            return LuaCallbackError(luastate, "internal error: no packet alert or signature");
-    }
-    return LuaCallbackRuleActionPushToStackFromSignature(luastate, s);
-}
-
-/** \internal
- *  \brief fill lua stack with signature info
- *  \param luastate the lua state
- *  \param s pointer to signature struct
- *  \retval cnt number of data items placed on the stack
- *
- *  Places: msg (string)
- */
-static int LuaCallbackRuleMsgPushToStackFromSignature(lua_State *luastate, const Signature *s)
-{
-    lua_pushstring(luastate, s->msg);
-    return 1;
-}
-
-/** \internal
- *  \brief Wrapper for getting tuple info into a lua script
- *  \retval cnt number of items placed on the stack
- *
- *  Info is pulled from PacketAlert if it exists in lua registry (true for logging scripts)
- *  otherwise pulled from Signature in lua registry (for match scripts)
- */
-static int LuaCallbackRuleMsg(lua_State *luastate)
-{
-    const Signature *s = NULL;
-    const PacketAlert *pa = LuaStateGetPacketAlert(luastate);
-    if (pa != NULL) {
-        s = pa->s;
-    } else {
-        s = LuaStateGetSignature(luastate);
-        if (s == NULL)
-            return LuaCallbackError(luastate, "internal error: no packet alert or signature");
-    }
-    return LuaCallbackRuleMsgPushToStackFromSignature(luastate, s);
-}
-
-/** \internal
- *  \brief fill lua stack with signature info
- *  \param luastate the lua state
- *  \param s pointer to signature struct
- *  \retval cnt number of data items placed on the stack
- *
- *  Places: class (string), prio (number)
- */
-static int LuaCallbackRuleClassPushToStackFromSignature(lua_State *luastate, const Signature *s)
-{
-    lua_pushstring(luastate, s->class_msg);
-    lua_pushinteger(luastate, s->prio);
-    return 2;
-}
-
-/** \internal
- *  \brief Wrapper for getting tuple info into a lua script
- *  \retval cnt number of items placed on the stack
- *
- *  Info is pulled from PacketAlert if it exists in lua registry (true for logging scripts)
- *  otherwise pulled from Signature in lua registry (for match scripts)
- */
-static int LuaCallbackRuleClass(lua_State *luastate)
-{
-    const Signature *s = NULL;
-    const PacketAlert *pa = LuaStateGetPacketAlert(luastate);
-    if (pa != NULL) {
-        s = pa->s;
-    } else {
-        s = LuaStateGetSignature(luastate);
-        if (s == NULL)
-            return LuaCallbackError(luastate, "internal error: no packet alert or signature");
-    }
-    return LuaCallbackRuleClassPushToStackFromSignature(luastate, s);
-}
-
 static int LuaCallbackLogPath(lua_State *luastate)
 {
     const char *ld = SCConfigGetLogDirectory();
@@ -537,16 +383,6 @@ int LuaRegisterFunctions(lua_State *luastate)
     lua_pushcfunction(luastate, LuaCallbackLogError);
     lua_setglobal(luastate, "SCLogError");
 
-
-    lua_pushcfunction(luastate, LuaCallbackRuleIds);
-    lua_setglobal(luastate, "SCRuleIds");
-    lua_pushcfunction(luastate, LuaCallbackRuleAction);
-    lua_setglobal(luastate, "SCRuleAction");
-    lua_pushcfunction(luastate, LuaCallbackRuleMsg);
-    lua_setglobal(luastate, "SCRuleMsg");
-    lua_pushcfunction(luastate, LuaCallbackRuleClass);
-    lua_setglobal(luastate, "SCRuleClass");
-
     lua_pushcfunction(luastate, LuaCallbackFileInfo);
     lua_setglobal(luastate, "SCFileInfo");
     lua_pushcfunction(luastate, LuaCallbackFileState);
diff --git a/src/util-lua-rule.c b/src/util-lua-rule.c
new file mode 100644 (file)
index 0000000..320f43f
--- /dev/null
@@ -0,0 +1,180 @@
+/* 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 "action-globals.h"
+#include "app-layer.h"
+#include "util-lua-rule.h"
+#include "util-lua-common.h"
+#include "util-lua.h"
+
+#include "lauxlib.h"
+
+static const char suricata_rule_mt[] = "suricata:rule:mt";
+
+static int LuaRuleGetRule(lua_State *L)
+{
+    const PacketAlert *pa = LuaStateGetPacketAlert(L);
+    const Signature *s = NULL;
+    if (pa != NULL) {
+        s = pa->s;
+    } else {
+        s = LuaStateGetSignature(L);
+    }
+    if (s == NULL) {
+        return LuaCallbackError(L, "internal error: no packet alert or signature");
+    }
+
+    void **p = lua_newuserdata(L, sizeof(*p));
+    if (p == NULL) {
+        return LuaCallbackError(L, "error: failed to allocate user data");
+    }
+    *p = (void *)s;
+
+    luaL_getmetatable(L, suricata_rule_mt);
+    lua_setmetatable(L, -2);
+
+    return 1;
+}
+
+static int LuaRuleGetSid(lua_State *L)
+{
+    void **data = luaL_testudata(L, 1, suricata_rule_mt);
+    if (data == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+    const Signature *s = *data;
+    lua_pushinteger(L, s->id);
+    return 1;
+}
+
+static int LuaRuleGetGid(lua_State *L)
+{
+    void **data = luaL_testudata(L, 1, suricata_rule_mt);
+    if (data == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+    const Signature *s = *data;
+    lua_pushinteger(L, s->gid);
+    return 1;
+}
+
+static int LuaRuleGetRev(lua_State *L)
+{
+    void **data = luaL_testudata(L, 1, suricata_rule_mt);
+    if (data == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+    const Signature *s = *data;
+    lua_pushinteger(L, s->rev);
+    return 1;
+}
+
+static int LuaRuleGetAction(lua_State *L)
+{
+    void **data = luaL_testudata(L, 1, suricata_rule_mt);
+    if (data == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+    const Signature *s = *data;
+
+    const char *action = "";
+    if (s->action & ACTION_PASS) {
+        action = "pass";
+    } else if ((s->action & ACTION_REJECT) || (s->action & ACTION_REJECT_BOTH) ||
+               (s->action & ACTION_REJECT_DST)) {
+        action = "reject";
+    } else if (s->action & ACTION_DROP) {
+        action = "drop";
+    } else if (s->action & ACTION_ALERT) {
+        action = "alert";
+    }
+    lua_pushstring(L, action);
+    return 1;
+}
+
+static int LuaRuleGetMsg(lua_State *L)
+{
+    void **data = luaL_testudata(L, 1, suricata_rule_mt);
+    if (data == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+    const Signature *s = *data;
+    lua_pushstring(L, s->msg);
+    return 1;
+}
+
+static int LuaRuleGetClassDescription(lua_State *L)
+{
+    void **data = luaL_testudata(L, 1, suricata_rule_mt);
+    if (data == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+    const Signature *s = *data;
+    lua_pushstring(L, s->class_msg);
+    return 1;
+}
+
+static int LuaRuleGetPriority(lua_State *L)
+{
+    void **data = luaL_testudata(L, 1, suricata_rule_mt);
+    if (data == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+    const Signature *s = *data;
+    lua_pushinteger(L, s->prio);
+    return 1;
+}
+
+static const struct luaL_Reg rulemt[] = {
+    // clang-format off
+    { "action", LuaRuleGetAction },
+    { "class_description", LuaRuleGetClassDescription, },
+    { "gid", LuaRuleGetGid, },
+    { "msg", LuaRuleGetMsg },
+    { "priority", LuaRuleGetPriority },
+    { "rev", LuaRuleGetRev, },
+    { "sid", LuaRuleGetSid, },
+    { NULL, NULL },
+    // clang-format on
+};
+
+static const struct luaL_Reg rulelib[] = {
+    // clang-format off
+    { "get_rule", LuaRuleGetRule, },
+    { NULL, NULL, }
+    // clang-format on
+};
+
+int SCLuaLoadRuleLib(lua_State *L)
+{
+    luaL_newmetatable(L, suricata_rule_mt);
+    lua_pushvalue(L, -1);
+    lua_setfield(L, -2, "__index");
+    luaL_setfuncs(L, rulemt, 0);
+
+    luaL_newlib(L, rulelib);
+
+    return 1;
+}
diff --git a/src/util-lua-rule.h b/src/util-lua-rule.h
new file mode 100644 (file)
index 0000000..56a9f0f
--- /dev/null
@@ -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_RULE_H
+#define SURICATA_UTIL_LUA_RULE_H
+
+#include "lua.h"
+
+int SCLuaLoadRuleLib(lua_State *L);
+
+#endif /* SURICATA_UTIL_LUA_RULE_H */