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"
30 #include "detect-engine-buffer.h"
31 #include "detect-parse.h"
32 #include "detect-lua.h"
34 #include "detect-lua-extensions.h"
35 
36 #include "util-lua.h"
37 #include "util-lua-common.h"
38 #include "util-lua-builtins.h"
39 
40 static int DetectTransformLuaxformSetup(DetectEngineCtx *, Signature *, const char *);
41 static void DetectTransformLuaxformFree(DetectEngineCtx *de_ctx, void *ptr);
42 static void TransformLuaxform(
43  DetectEngineThreadCtx *det_ctx, InspectionBuffer *buffer, void *options);
44 
45 #define LUAXFORM_MAX_ARGS 10
46 
47 typedef struct DetectLuaxformData {
50  int arg_count;
51  uint64_t alloc_limit;
53  const char *filename;
54  char *copystr;
55  const char *id_data;
56  uint32_t id_data_len;
57  const char *args[LUAXFORM_MAX_ARGS];
59 
60 typedef struct DetectLuaxformThreadData {
63 
64 static 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 
73 static 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 
95 static int DetectTransformLuaxformSetupPrime(
97 {
99  if (luastate == NULL)
100  return -1;
101  if (ld->allow_restricted_functions) {
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 
130 error:
131  SCLuaSbStateClose(luastate);
132  return -1;
133 }
134 
135 static 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 
176 error:
177  if (lua != NULL)
178  DetectTransformLuaxformFree(de_ctx, lua);
179  return NULL;
180 }
181 
182 static void *DetectLuaxformThreadInit(void *data)
183 {
184  /* Note: This will always be non-null as alloc errors are checked before registering callback */
185  DetectLuaxformData *lua = (DetectLuaxformData *)data;
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 
198  if (lua->allow_restricted_functions) {
199  luaL_openlibs(t->luastate);
201  } else {
203  }
204 
206 
207  int status = luaL_loadfile(t->luastate, lua->filename);
208  if (status) {
209  SCLogError("couldn't load file: %s", lua_tostring(t->luastate, -1));
210  goto error;
211  }
212 
213  /* prime the script (or something) */
214  if (lua_pcall(t->luastate, 0, 0, 0) != 0) {
215  SCLogError("couldn't prime file: %s", lua_tostring(t->luastate, -1));
216  goto error;
217  }
218 
219  /* when present: thread_init call */
220  lua_getglobal(t->luastate, "thread_init");
221  if (lua_isfunction(t->luastate, -1)) {
222  if (lua_pcall(t->luastate, 0, 0, 0) != 0) {
223  SCLogError("couldn't run script 'thread_init' function: %s",
224  lua_tostring(t->luastate, -1));
225  goto error;
226  }
227  } else {
228  lua_pop(t->luastate, 1);
229  }
230 
231  return (void *)t;
232 
233 error:
234  if (t->luastate != NULL)
236  SCFree(t);
237  return NULL;
238 }
239 
240 static void DetectLuaxformThreadFree(void *ctx)
241 {
242  if (ctx != NULL) {
244  if (t->luastate != NULL)
246  SCFree(t);
247  }
248 }
249 
250 /**
251  * \internal
252  * \brief Apply the luaxform keyword to the last pattern match
253  * \param de_ctx detection engine ctx
254  * \param s signature
255  * \param str lua filename and optional args
256  * \retval 0 ok
257  * \retval -1 failure
258  */
259 static int DetectTransformLuaxformSetup(DetectEngineCtx *de_ctx, Signature *s, const char *optsstr)
260 {
261  SCEnter();
262 
263  /* First check if Lua rules are enabled, by default Lua in rules
264  * is disabled. */
265  int enabled = 0;
266  (void)SCConfGetBool("security.lua.allow-rules", &enabled);
267  if (!enabled) {
268  SCLogError("Lua rules disabled by security configuration: security.lua.allow-rules");
269  SCReturnInt(-1);
270  }
271 
272  DetectLuaxformData *lua = DetectLuaxformParse(de_ctx, optsstr);
273  if (lua == NULL)
274  goto error;
275 
276  /* Load lua sandbox configurations */
277  intmax_t lua_alloc_limit = DEFAULT_LUA_ALLOC_LIMIT;
278  intmax_t lua_instruction_limit = DEFAULT_LUA_INSTRUCTION_LIMIT;
279  int allow_restricted_functions = 0;
280  (void)SCConfGetInt("security.lua.max-bytes", &lua_alloc_limit);
281  (void)SCConfGetInt("security.lua.max-instructions", &lua_instruction_limit);
282  (void)SCConfGetBool("security.lua.allow-restricted-functions", &allow_restricted_functions);
283 
284  lua->alloc_limit = lua_alloc_limit;
285  lua->instruction_limit = lua_instruction_limit;
286  lua->allow_restricted_functions = allow_restricted_functions;
287 
288  if (DetectTransformLuaxformSetupPrime(de_ctx, lua, s) == -1) {
289  goto error;
290  }
291 
293  de_ctx, "luaxform", DetectLuaxformThreadInit, (void *)lua, DetectLuaxformThreadFree, 0);
294  if (lua->thread_ctx_id == -1)
295  goto error;
296 
298  SCReturnInt(0);
299 
300 error:
301 
302  if (lua != NULL)
303  DetectTransformLuaxformFree(de_ctx, lua);
304  SCReturnInt(-1);
305 }
306 
307 static void TransformLuaxform(
308  DetectEngineThreadCtx *det_ctx, InspectionBuffer *buffer, void *options)
309 {
310  if (buffer->inspect_len == 0) {
311  return;
312  }
313 
314  DetectLuaxformData *lua = options;
315  DetectLuaThreadData *tlua =
317  if (tlua == NULL) {
318  return;
319  }
320 
321  lua_getglobal(tlua->luastate, "transform");
322 
323  const uint8_t *input = buffer->inspect;
324  const uint32_t input_len = buffer->inspect_len;
325 
326  /* Lua script args are: buffer, rule args table */
327  LuaPushStringBuffer(tlua->luastate, input, (size_t)input_len);
328  /*
329  * Add provided arguments for lua script (these are optionally
330  * provided by the rule writer).
331  *
332  * Start at offset 1 (arg[0] is the lua script filename)
333  */
334  lua_newtable(tlua->luastate);
335  for (int i = 1; i < lua->arg_count + 1; i++) {
336  LuaPushInteger(tlua->luastate, i);
337  lua_pushstring(tlua->luastate, lua->args[i]);
338  lua_settable(tlua->luastate, -3);
339  }
340 
342 
343  if (LUA_OK != lua_pcall(tlua->luastate, 2, 2, 0)) {
344  SCLogDebug("error calling lua script: %s", lua_tostring(tlua->luastate, -1));
345  } else {
346  /* Lua transform functions must return 2 values: buffer and length */
347  int return_value_count = lua_gettop(tlua->luastate);
348  if (return_value_count != 2) {
349  SCLogDebug("Error: expected 2 return values but got %d", return_value_count);
350  goto error;
351  }
352 
353  if (lua_isstring(tlua->luastate, -2)) {
354  const char *transformed_buffer = lua_tostring(tlua->luastate, -2);
355  lua_Integer transformed_buffer_byte_count = lua_tointeger(tlua->luastate, -1);
356  if (transformed_buffer != NULL && transformed_buffer_byte_count > 0)
357  InspectionBufferCopy(buffer, (uint8_t *)transformed_buffer,
358  (uint32_t)transformed_buffer_byte_count);
359  SCLogDebug("transform returns [nbytes %d] \"%p\"",
360  (uint32_t)transformed_buffer_byte_count, transformed_buffer);
361  }
362  }
363 
364 error:
365  while (lua_gettop(tlua->luastate) > 0) {
366  lua_pop(tlua->luastate, 1);
367  }
368 }
369 
371 {
374  "pass inspection buffer to a Lua function along with "
375  "arguments supplied to the transform";
376  sigmatch_table[DETECT_TRANSFORM_LUAXFORM].url = "/rules/transforms.html#luaxform";
378  sigmatch_table[DETECT_TRANSFORM_LUAXFORM].Free = DetectTransformLuaxformFree;
379  sigmatch_table[DETECT_TRANSFORM_LUAXFORM].Setup = DetectTransformLuaxformSetup;
381  sigmatch_table[DETECT_TRANSFORM_LUAXFORM].TransformId = DetectTransformLuaxformId;
382 }
SigTableElmt_::url
const char * url
Definition: detect.h:1422
detect-engine.h
SigTableElmt_::desc
const char * desc
Definition: detect.h:1421
sigmatch_table
SigTableElmt * sigmatch_table
Definition: detect-parse.c:79
util-lua-common.h
SigTableElmt_::Free
void(* Free)(DetectEngineCtx *, void *)
Definition: detect.h:1409
SigTableElmt_::name
const char * name
Definition: detect.h:1419
DetectThreadCtxGetKeywordThreadCtx
void * DetectThreadCtxGetKeywordThreadCtx(DetectEngineThreadCtx *det_ctx, int id)
Retrieve thread local keyword ctx by id.
Definition: detect-engine.c:3704
InspectionBufferCopy
void InspectionBufferCopy(InspectionBuffer *buffer, uint8_t *buf, uint32_t buf_len)
Definition: detect-engine-inspect-buffer.c:246
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:270
SIGMATCH_QUOTES_OPTIONAL
#define SIGMATCH_QUOTES_OPTIONAL
Definition: detect.h:1624
util-lua.h
DetectLuaxformData::arg_count
int arg_count
Definition: detect-transform-luaxform.c:50
InspectionBuffer
Definition: detect-engine-inspect-buffer.h:34
DetectLuaxformData::id_data_len
uint32_t id_data_len
Definition: detect-transform-luaxform.c:56
DetectLuaxformData::allow_restricted_functions
int allow_restricted_functions
Definition: detect-transform-luaxform.c:49
SigTableElmt_::flags
uint16_t flags
Definition: detect.h:1413
ctx
struct Thresholds ctx
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:919
DetectLuaxformData::thread_ctx_id
int thread_ctx_id
Definition: detect-transform-luaxform.c:48
detect-transform-luaxform.h
DEFAULT_LUA_INSTRUCTION_LIMIT
#define DEFAULT_LUA_INSTRUCTION_LIMIT
Definition: detect-lua.c:124
detect-lua.h
SCConfGetBool
int SCConfGetBool(const char *name, int *val)
Retrieve a configuration value as a boolean.
Definition: conf.c:497
DetectLuaxformData::filename
const char * filename
Definition: detect-transform-luaxform.c:53
util-lua-builtins.h
SigTableElmt_::Setup
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1404
DetectLuaThreadData::luastate
lua_State * luastate
Definition: detect-lua.h:31
lua_State
struct lua_State lua_State
Definition: suricata-common.h:523
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:18
DetectEngineThreadCtx_
Definition: detect.h:1211
SCConfGetInt
int SCConfGetInt(const char *name, intmax_t *val)
Retrieve a configuration value as an integer.
Definition: conf.c:414
SCEnter
#define SCEnter(...)
Definition: util-debug.h:272
detect.h
DetectLuaxformData
Definition: detect-transform-luaxform.c:47
DetectTransformLuaxformRegister
void DetectTransformLuaxformRegister(void)
Definition: detect-transform-luaxform.c:370
DetectLuaxformData::copystr
char * copystr
Definition: detect-transform-luaxform.c:54
SCLuaSbStateClose
void SCLuaSbStateClose(lua_State *L)
Definition: util-lua-sandbox.c:360
SCLuaRequirefBuiltIns
void SCLuaRequirefBuiltIns(lua_State *L)
Register Suricata built-in modules for loading in a non-sandboxed environment.
Definition: util-lua-builtins.c:79
DetectLuaThreadData
Definition: detect-lua.h:30
SCLuaSbLoadLibs
void SCLuaSbLoadLibs(lua_State *L)
Definition: util-lua-sandbox.c:279
DetectLuaxformData
struct DetectLuaxformData DetectLuaxformData
DetectRegisterThreadCtxFuncs
int DetectRegisterThreadCtxFuncs(DetectEngineCtx *de_ctx, const char *name, void *(*InitFunc)(void *), void *data, void(*FreeFunc)(void *), int mode)
Register Thread keyword context Funcs.
Definition: detect-engine.c:3634
detect-lua-extensions.h
suricata-common.h
DEFAULT_LUA_ALLOC_LIMIT
#define DEFAULT_LUA_ALLOC_LIMIT
Definition: detect-lua.c:123
detect-engine-buffer.h
SCLuaSbResetInstructionCounter
void SCLuaSbResetInstructionCounter(lua_State *L)
Definition: util-lua-sandbox.c:387
DETECT_TRANSFORM_LUAXFORM
@ DETECT_TRANSFORM_LUAXFORM
Definition: detect-engine-register.h:318
DetectLuaxformThreadData
Definition: detect-transform-luaxform.c:60
FatalError
#define FatalError(...)
Definition: util-debug.h:503
DetectLuaxformData::args
const char * args[LUAXFORM_MAX_ARGS]
Definition: detect-transform-luaxform.c:57
DetectLuaxformData::instruction_limit
uint64_t instruction_limit
Definition: detect-transform-luaxform.c:52
InspectionBuffer::inspect_len
uint32_t inspect_len
Definition: detect-engine-inspect-buffer.h:37
InspectionBuffer::inspect
const uint8_t * inspect
Definition: detect-engine-inspect-buffer.h:35
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:262
SCFree
#define SCFree(p)
Definition: util-mem.h:61
LUAXFORM_MAX_ARGS
#define LUAXFORM_MAX_ARGS
Definition: detect-transform-luaxform.c:45
detect-parse.h
Signature_
Signature container.
Definition: detect.h:657
DetectLuaxformData::id_data
const char * id_data
Definition: detect-transform-luaxform.c:55
SCDetectSignatureAddTransform
int SCDetectSignatureAddTransform(Signature *s, int transform, void *options)
Definition: detect-engine-buffer.c:178
LuaRegisterExtensions
int LuaRegisterExtensions(lua_State *lua_state)
Register Suricata Lua functions.
Definition: detect-lua-extensions.c:116
DetectLoadCompleteSigPath
char * DetectLoadCompleteSigPath(const DetectEngineCtx *de_ctx, const char *sig_file)
Create the path if default-rule-path was specified.
Definition: detect-engine-loader.c:106
SCLuaSbStateNew
lua_State * SCLuaSbStateNew(uint64_t alloclimit, uint64_t instructionlimit)
Allocate a new Lua sandbox.
Definition: util-lua-sandbox.c:319
DetectUnregisterThreadCtxFuncs
int DetectUnregisterThreadCtxFuncs(DetectEngineCtx *de_ctx, void *data, const char *name)
Remove Thread keyword context registration.
Definition: detect-engine.c:3686
DetectLuaxformThreadData::luastate
lua_State * luastate
Definition: detect-transform-luaxform.c:61
DetectLuaxformThreadData
struct DetectLuaxformThreadData DetectLuaxformThreadData
SigTableElmt_::Transform
void(* Transform)(DetectEngineThreadCtx *, InspectionBuffer *, void *context)
Definition: detect.h:1397
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCReturnInt
#define SCReturnInt(x)
Definition: util-debug.h:276
SigTableElmt_::TransformId
void(* TransformId)(const uint8_t **data, uint32_t *length, void *context)
Definition: detect.h:1401
DetectLuaxformData::alloc_limit
uint64_t alloc_limit
Definition: detect-transform-luaxform.c:51
LuaPushInteger
int LuaPushInteger(lua_State *luastate, lua_Integer n)
Definition: util-lua.c:340
LuaPushStringBuffer
int LuaPushStringBuffer(lua_State *luastate, const uint8_t *input, size_t input_len)
Definition: util-lua.c:319