suricata
detect-transform-luaxform.c
Go to the documentation of this file.
1/* Copyright (C) 2024 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18/**
19 * \file
20 *
21 * \author Jeff Lucovsky <jlucovsky@oisf.net>
22 *
23 * Implements the luxaform transform keyword
24 */
25
26#include "suricata-common.h"
27
28#include "detect.h"
29#include "detect-engine.h"
31#include "detect-parse.h"
32#include "detect-lua.h"
35
36#include "util-lua.h"
37#include "util-lua-common.h"
38#include "util-lua-builtins.h"
39
40static int DetectTransformLuaxformSetup(DetectEngineCtx *, Signature *, const char *);
41static void DetectTransformLuaxformFree(DetectEngineCtx *de_ctx, void *ptr);
42static void TransformLuaxform(
43 DetectEngineThreadCtx *det_ctx, InspectionBuffer *buffer, void *options);
44
45#define LUAXFORM_MAX_ARGS 10
46
59
63
64static void DetectTransformLuaxformId(const uint8_t **data, uint32_t *length, void *context)
65{
66 if (context) {
67 DetectLuaxformData *lua = (DetectLuaxformData *)context;
68 *data = (uint8_t *)lua->id_data;
69 *length = lua->id_data_len;
70 }
71}
72
73static void DetectTransformLuaxformFree(DetectEngineCtx *de_ctx, void *ptr)
74{
75 if (ptr != NULL) {
77
78 if (lua->filename)
79 SCFree((void *)lua->filename);
80
81 if (lua->copystr)
82 SCFree((void *)lua->copystr);
83
84 if (lua->id_data)
85 SCFree((void *)lua->id_data);
86
87 if (de_ctx) {
88 DetectUnregisterThreadCtxFuncs(de_ctx, lua, "luaxform");
89 }
90
91 SCFree(lua);
92 }
93}
94
95static int DetectTransformLuaxformSetupPrime(
97{
99 if (luastate == NULL)
100 return -1;
102 luaL_openlibs(luastate);
103 SCLuaRequirefBuiltIns(luastate);
104 } else {
105 SCLuaSbLoadLibs(luastate);
106 }
107
108 int status = luaL_loadfile(luastate, ld->filename);
109 if (status) {
110 SCLogError("couldn't load file: %s", lua_tostring(luastate, -1));
111 goto error;
112 }
113
114 /* prime the script (or something) */
115 if (lua_pcall(luastate, 0, 0, 0) != 0) {
116 SCLogError("couldn't prime file: %s", lua_tostring(luastate, -1));
117 goto error;
118 }
119
120 lua_getglobal(luastate, "transform");
121 if (lua_type(luastate, -1) != LUA_TFUNCTION) {
122 SCLogError("no transform function in script");
123 goto error;
124 }
125 lua_pop(luastate, 1);
126
127 SCLuaSbStateClose(luastate);
128 return 0;
129
130error:
131 SCLuaSbStateClose(luastate);
132 return -1;
133}
134
135static DetectLuaxformData *DetectLuaxformParse(DetectEngineCtx *de_ctx, const char *optsstr)
136{
137 DetectLuaxformData *lua = NULL;
138
139 /* We have a correct lua option */
140 lua = SCCalloc(1, sizeof(DetectLuaxformData));
141 if (unlikely(lua == NULL)) {
142 FatalError("unable to allocate memory for Lua transform: %s", optsstr);
143 }
144
145 lua->copystr = strdup(optsstr);
146 lua->id_data = strdup(optsstr);
147 if (unlikely(lua->copystr == NULL || lua->id_data == NULL)) {
148 FatalError("unable to allocate memory for Lua transform: %s", optsstr);
149 }
150
151 lua->id_data_len = (uint32_t)strlen(lua->id_data);
152
153 int count = 0;
154 char *saveptr = NULL;
155 char *token = strtok_r(lua->copystr, ",", &saveptr);
156 while (token != NULL && count < LUAXFORM_MAX_ARGS) {
157 lua->args[count++] = token;
158 token = strtok_r(NULL, ",", &saveptr);
159 }
160
161 if (count == 0) {
162 SCLogError("Lua script name not supplied");
163 goto error;
164 }
165
166 lua->arg_count = count - 1;
167
168 /* get full filename */
170 if (lua->filename == NULL) {
171 goto error;
172 }
173
174 return lua;
175
176error:
177 if (lua != NULL)
178 DetectTransformLuaxformFree(de_ctx, lua);
179 return NULL;
180}
181
182static void *DetectLuaxformThreadInit(void *data)
183{
184 /* Note: This will always be non-null as alloc errors are checked before registering callback */
186
188 if (unlikely(t == NULL)) {
189 FatalError("unable to allocate luaxform context memory");
190 }
191
193 if (t->luastate == NULL) {
194 SCLogError("luastate pool depleted");
195 goto error;
196 }
197
199 luaL_openlibs(t->luastate);
201 } else {
203 }
204
205 int status = luaL_loadfile(t->luastate, lua->filename);
206 if (status) {
207 SCLogError("couldn't load file: %s", lua_tostring(t->luastate, -1));
208 goto error;
209 }
210
211 /* prime the script (or something) */
212 if (lua_pcall(t->luastate, 0, 0, 0) != 0) {
213 SCLogError("couldn't prime file: %s", lua_tostring(t->luastate, -1));
214 goto error;
215 }
216
217 /* when present: thread_init call */
218 lua_getglobal(t->luastate, "thread_init");
219 if (lua_isfunction(t->luastate, -1)) {
220 if (lua_pcall(t->luastate, 0, 0, 0) != 0) {
221 SCLogError("couldn't run script 'thread_init' function: %s",
222 lua_tostring(t->luastate, -1));
223 goto error;
224 }
225 } else {
226 lua_pop(t->luastate, 1);
227 }
228
229 return (void *)t;
230
231error:
232 if (t->luastate != NULL)
234 SCFree(t);
235 return NULL;
236}
237
238static void DetectLuaxformThreadFree(void *ctx)
239{
240 if (ctx != NULL) {
242 if (t->luastate != NULL)
244 SCFree(t);
245 }
246}
247
248/**
249 * \internal
250 * \brief Apply the luaxform keyword to the last pattern match
251 * \param de_ctx detection engine ctx
252 * \param s signature
253 * \param str lua filename and optional args
254 * \retval 0 ok
255 * \retval -1 failure
256 */
257static int DetectTransformLuaxformSetup(DetectEngineCtx *de_ctx, Signature *s, const char *optsstr)
258{
259 SCEnter();
260
261 /* First check if Lua rules are enabled, by default Lua in rules
262 * is disabled. */
263 int enabled = 0;
264 (void)SCConfGetBool("security.lua.allow-rules", &enabled);
265 if (!enabled) {
266 SCLogError("Lua rules disabled by security configuration: security.lua.allow-rules");
267 SCReturnInt(-1);
268 }
269
270 DetectLuaxformData *lua = DetectLuaxformParse(de_ctx, optsstr);
271 if (lua == NULL)
272 goto error;
273
274 /* Load lua sandbox configurations */
275 intmax_t lua_alloc_limit = DEFAULT_LUA_ALLOC_LIMIT;
276 intmax_t lua_instruction_limit = DEFAULT_LUA_INSTRUCTION_LIMIT;
277 int allow_restricted_functions = 0;
278 (void)SCConfGetInt("security.lua.max-bytes", &lua_alloc_limit);
279 (void)SCConfGetInt("security.lua.max-instructions", &lua_instruction_limit);
280 (void)SCConfGetBool("security.lua.allow-restricted-functions", &allow_restricted_functions);
281
282 lua->alloc_limit = lua_alloc_limit;
283 lua->instruction_limit = lua_instruction_limit;
284 lua->allow_restricted_functions = allow_restricted_functions;
285
286 if (DetectTransformLuaxformSetupPrime(de_ctx, lua, s) == -1) {
287 goto error;
288 }
289
291 de_ctx, "luaxform", DetectLuaxformThreadInit, (void *)lua, DetectLuaxformThreadFree, 0);
292 if (lua->thread_ctx_id == -1)
293 goto error;
294
296 SCReturnInt(0);
297
298error:
299
300 if (lua != NULL)
301 DetectTransformLuaxformFree(de_ctx, lua);
302 SCReturnInt(-1);
303}
304
305static void TransformLuaxform(
306 DetectEngineThreadCtx *det_ctx, InspectionBuffer *buffer, void *options)
307{
308 if (buffer->inspect_len == 0) {
309 return;
310 }
311
312 DetectLuaxformData *lua = options;
313 DetectLuaThreadData *tlua =
315 if (tlua == NULL) {
316 return;
317 }
318
319 lua_getglobal(tlua->luastate, "transform");
320
321 const uint8_t *input = buffer->inspect;
322 const uint32_t input_len = buffer->inspect_len;
323
324 /* Lua script args are: buffer, rule args table */
325 LuaPushStringBuffer(tlua->luastate, input, (size_t)input_len);
326 /*
327 * Add provided arguments for lua script (these are optionally
328 * provided by the rule writer).
329 *
330 * Start at offset 1 (arg[0] is the lua script filename)
331 */
332 lua_newtable(tlua->luastate);
333 for (int i = 1; i < lua->arg_count + 1; i++) {
334 LuaPushInteger(tlua->luastate, i);
335 lua_pushstring(tlua->luastate, lua->args[i]);
336 lua_settable(tlua->luastate, -3);
337 }
338
340
341 if (LUA_OK != lua_pcall(tlua->luastate, 2, 2, 0)) {
342 SCLogDebug("error calling lua script: %s", lua_tostring(tlua->luastate, -1));
343 } else {
344 /* Lua transform functions must return 2 values: buffer and length */
345 int return_value_count = lua_gettop(tlua->luastate);
346 if (return_value_count != 2) {
347 SCLogDebug("Error: expected 2 return values but got %d", return_value_count);
348 goto error;
349 }
350
351 if (lua_isstring(tlua->luastate, -2)) {
352 const char *transformed_buffer = lua_tostring(tlua->luastate, -2);
353 lua_Integer transformed_buffer_byte_count = lua_tointeger(tlua->luastate, -1);
354 if (transformed_buffer != NULL && transformed_buffer_byte_count > 0)
355 InspectionBufferCopy(buffer, (uint8_t *)transformed_buffer,
356 (uint32_t)transformed_buffer_byte_count);
357 SCLogDebug("transform returns [nbytes %d] \"%p\"",
358 (uint32_t)transformed_buffer_byte_count, transformed_buffer);
359 }
360 }
361
362error:
363 while (lua_gettop(tlua->luastate) > 0) {
364 lua_pop(tlua->luastate, 1);
365 }
366}
367
369{
372 "pass inspection buffer to a Lua function along with "
373 "arguments supplied to the transform";
374 sigmatch_table[DETECT_TRANSFORM_LUAXFORM].url = "/rules/transforms.html#luaxform";
376 sigmatch_table[DETECT_TRANSFORM_LUAXFORM].Free = DetectTransformLuaxformFree;
377 sigmatch_table[DETECT_TRANSFORM_LUAXFORM].Setup = DetectTransformLuaxformSetup;
379 sigmatch_table[DETECT_TRANSFORM_LUAXFORM].TransformId = DetectTransformLuaxformId;
380}
int SCConfGetInt(const char *name, intmax_t *val)
Retrieve a configuration value as an integer.
Definition conf.c:414
int SCConfGetBool(const char *name, int *val)
Retrieve a configuration value as a boolean.
Definition conf.c:497
int SCDetectSignatureAddTransform(Signature *s, int transform, void *options)
void InspectionBufferCopy(InspectionBuffer *buffer, uint8_t *buf, uint32_t buf_len)
char * DetectLoadCompleteSigPath(const DetectEngineCtx *de_ctx, const char *sig_file)
Create the path if default-rule-path was specified.
@ DETECT_TRANSFORM_LUAXFORM
void * DetectThreadCtxGetKeywordThreadCtx(DetectEngineThreadCtx *det_ctx, int id)
Retrieve thread local keyword ctx by id.
int DetectRegisterThreadCtxFuncs(DetectEngineCtx *de_ctx, const char *name, void *(*InitFunc)(void *), void *data, void(*FreeFunc)(void *), int mode)
Register Thread keyword context Funcs.
int DetectUnregisterThreadCtxFuncs(DetectEngineCtx *de_ctx, void *data, const char *name)
Remove Thread keyword context registration.
#define DEFAULT_LUA_INSTRUCTION_LIMIT
Definition detect-lua.c:124
#define DEFAULT_LUA_ALLOC_LIMIT
Definition detect-lua.c:123
SigTableElmt * sigmatch_table
void DetectTransformLuaxformRegister(void)
#define LUAXFORM_MAX_ARGS
#define SIGMATCH_QUOTES_OPTIONAL
Definition detect.h:1664
DetectEngineCtx * de_ctx
struct Thresholds ctx
main detection engine ctx
Definition detect.h:932
lua_State * luastate
Definition detect-lua.h:31
const char * args[LUAXFORM_MAX_ARGS]
const char * url
Definition detect.h:1462
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition detect.h:1441
void(* Free)(DetectEngineCtx *, void *)
Definition detect.h:1446
uint16_t flags
Definition detect.h:1450
const char * desc
Definition detect.h:1461
void(* TransformId)(const uint8_t **data, uint32_t *length, void *context)
Definition detect.h:1438
const char * name
Definition detect.h:1459
void(* Transform)(DetectEngineThreadCtx *, InspectionBuffer *, void *context)
Definition detect.h:1434
Signature container.
Definition detect.h:668
struct lua_State lua_State
#define SCEnter(...)
Definition util-debug.h:277
#define FatalError(...)
Definition util-debug.h:510
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCReturnInt(x)
Definition util-debug.h:281
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
void SCLuaRequirefBuiltIns(lua_State *L)
Register Suricata built-in modules for loading in a non-sandboxed environment.
void SCLuaSbStateClose(lua_State *L)
void SCLuaSbLoadLibs(lua_State *L)
lua_State * SCLuaSbStateNew(uint64_t alloclimit, uint64_t instructionlimit)
Allocate a new Lua sandbox.
void SCLuaSbResetInstructionCounter(lua_State *L)
int LuaPushStringBuffer(lua_State *luastate, const uint8_t *input, size_t input_len)
Definition util-lua.c:319
int LuaPushInteger(lua_State *luastate, lua_Integer n)
Definition util-lua.c:340
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define unlikely(expr)