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 
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 
231 error:
232  if (t->luastate != NULL)
234  SCFree(t);
235  return NULL;
236 }
237 
238 static 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  */
257 static 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 
298 error:
299 
300  if (lua != NULL)
301  DetectTransformLuaxformFree(de_ctx, lua);
302  SCReturnInt(-1);
303 }
304 
305 static 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 
362 error:
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 }
SigTableElmt_::url
const char * url
Definition: detect.h:1462
detect-engine.h
SigTableElmt_::desc
const char * desc
Definition: detect.h:1461
sigmatch_table
SigTableElmt * sigmatch_table
Definition: detect-parse.c:79
util-lua-common.h
SigTableElmt_::Free
void(* Free)(DetectEngineCtx *, void *)
Definition: detect.h:1446
SigTableElmt_::name
const char * name
Definition: detect.h:1459
DetectThreadCtxGetKeywordThreadCtx
void * DetectThreadCtxGetKeywordThreadCtx(DetectEngineThreadCtx *det_ctx, int id)
Retrieve thread local keyword ctx by id.
Definition: detect-engine.c:3733
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:275
SIGMATCH_QUOTES_OPTIONAL
#define SIGMATCH_QUOTES_OPTIONAL
Definition: detect.h:1664
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:1450
ctx
struct Thresholds ctx
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:932
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:1441
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:1244
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:277
detect.h
DetectLuaxformData
Definition: detect-transform-luaxform.c:47
DetectTransformLuaxformRegister
void DetectTransformLuaxformRegister(void)
Definition: detect-transform-luaxform.c:368
DetectLuaxformData::copystr
char * copystr
Definition: detect-transform-luaxform.c:54
SCLuaSbStateClose
void SCLuaSbStateClose(lua_State *L)
Definition: util-lua-sandbox.c:361
SCLuaRequirefBuiltIns
void SCLuaRequirefBuiltIns(lua_State *L)
Register Suricata built-in modules for loading in a non-sandboxed environment.
Definition: util-lua-builtins.c:85
DetectLuaThreadData
Definition: detect-lua.h:30
SCLuaSbLoadLibs
void SCLuaSbLoadLibs(lua_State *L)
Definition: util-lua-sandbox.c:280
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:3663
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:388
DETECT_TRANSFORM_LUAXFORM
@ DETECT_TRANSFORM_LUAXFORM
Definition: detect-engine-register.h:306
DetectLuaxformThreadData
Definition: detect-transform-luaxform.c:60
FatalError
#define FatalError(...)
Definition: util-debug.h:510
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:267
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:668
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
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:320
DetectUnregisterThreadCtxFuncs
int DetectUnregisterThreadCtxFuncs(DetectEngineCtx *de_ctx, void *data, const char *name)
Remove Thread keyword context registration.
Definition: detect-engine.c:3715
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:1434
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCReturnInt
#define SCReturnInt(x)
Definition: util-debug.h:281
SigTableElmt_::TransformId
void(* TransformId)(const uint8_t **data, uint32_t *length, void *context)
Definition: detect.h:1438
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