/src/suricata7/src/output-lua.c
Line | Count | Source |
1 | | /* Copyright (C) 2014-2022 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 Victor Julien <victor@inliniac.net> |
22 | | * |
23 | | */ |
24 | | |
25 | | #include "suricata-common.h" |
26 | | #include "output-lua.h" |
27 | | |
28 | | #ifdef HAVE_LUA |
29 | | #include "util-print.h" |
30 | | #include "util-unittest.h" |
31 | | #include "util-debug.h" |
32 | | #include "output.h" |
33 | | #include "app-layer-htp.h" |
34 | | #include "app-layer.h" |
35 | | #include "app-layer-ssl.h" |
36 | | #include "app-layer-ssh.h" |
37 | | #include "app-layer-parser.h" |
38 | | #include "util-privs.h" |
39 | | #include "util-buffer.h" |
40 | | #include "util-proto-name.h" |
41 | | #include "util-logopenfile.h" |
42 | | #include "util-time.h" |
43 | | #include "util-lua.h" |
44 | | #include "util-lua-common.h" |
45 | | #include "util-lua-http.h" |
46 | | #include "util-lua-dns.h" |
47 | | #include "util-lua-ja3.h" |
48 | | #include "util-lua-tls.h" |
49 | | #include "util-lua-ssh.h" |
50 | | #include "util-lua-hassh.h" |
51 | | #include "util-lua-smtp.h" |
52 | | |
53 | | #define MODULE_NAME "LuaLog" |
54 | | |
55 | | /** \brief structure containing global config |
56 | | * The OutputLuaLogInitSub which is run per script |
57 | | * can access this to get global config info through |
58 | | * it's parent_ctx->data ptr. |
59 | | */ |
60 | | typedef struct LogLuaMasterCtx_ { |
61 | | char path[PATH_MAX]; /**< contains script-dir */ |
62 | | } LogLuaMasterCtx; |
63 | | |
64 | | typedef struct LogLuaCtx_ { |
65 | | SCMutex m; |
66 | | lua_State *luastate; |
67 | | int deinit_once; |
68 | | } LogLuaCtx; |
69 | | |
70 | | typedef struct LogLuaThreadCtx_ { |
71 | | LogLuaCtx *lua_ctx; |
72 | | } LogLuaThreadCtx; |
73 | | |
74 | | static TmEcode LuaLogThreadInit(ThreadVars *t, const void *initdata, void **data); |
75 | | static TmEcode LuaLogThreadDeinit(ThreadVars *t, void *data); |
76 | | |
77 | | /** \internal |
78 | | * \brief TX logger for lua scripts |
79 | | * |
80 | | * A single call to this function will run one script on a single |
81 | | * transaction. |
82 | | * |
83 | | * NOTE: The flow (f) also referenced by p->flow is locked. |
84 | | */ |
85 | | static int LuaTxLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *alstate, void *txptr, uint64_t tx_id) |
86 | | { |
87 | | SCEnter(); |
88 | | |
89 | | LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data; |
90 | | |
91 | | SCMutexLock(&td->lua_ctx->m); |
92 | | |
93 | | LuaStateSetThreadVars(td->lua_ctx->luastate, tv); |
94 | | LuaStateSetPacket(td->lua_ctx->luastate, (Packet *)p); |
95 | | LuaStateSetTX(td->lua_ctx->luastate, txptr, tx_id); |
96 | | LuaStateSetFlow(td->lua_ctx->luastate, f); |
97 | | |
98 | | /* prepare data to pass to script */ |
99 | | lua_getglobal(td->lua_ctx->luastate, "log"); |
100 | | lua_newtable(td->lua_ctx->luastate); |
101 | | LuaPushTableKeyValueInt(td->lua_ctx->luastate, "tx_id", (int)(tx_id)); |
102 | | |
103 | | int retval = lua_pcall(td->lua_ctx->luastate, 1, 0, 0); |
104 | | if (retval != 0) { |
105 | | SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1)); |
106 | | } |
107 | | |
108 | | SCMutexUnlock(&td->lua_ctx->m); |
109 | | SCReturnInt(0); |
110 | | } |
111 | | |
112 | | /** \internal |
113 | | * \brief Streaming logger for lua scripts |
114 | | * |
115 | | * Hooks into the Streaming Logger API. Gets called for each chunk of new |
116 | | * streaming data. |
117 | | */ |
118 | | static int LuaStreamingLogger(ThreadVars *tv, void *thread_data, const Flow *f, |
119 | | const uint8_t *data, uint32_t data_len, uint64_t tx_id, uint8_t flags) |
120 | | { |
121 | | SCEnter(); |
122 | | |
123 | | void *txptr = NULL; |
124 | | LuaStreamingBuffer b = { data, data_len, flags }; |
125 | | |
126 | | SCLogDebug("flags %02x", flags); |
127 | | |
128 | | if (flags & OUTPUT_STREAMING_FLAG_TRANSACTION) { |
129 | | if (f && f->alstate) |
130 | | txptr = AppLayerParserGetTx(f->proto, f->alproto, f->alstate, tx_id); |
131 | | } |
132 | | |
133 | | LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data; |
134 | | |
135 | | SCMutexLock(&td->lua_ctx->m); |
136 | | |
137 | | LuaStateSetThreadVars(td->lua_ctx->luastate, tv); |
138 | | if (flags & OUTPUT_STREAMING_FLAG_TRANSACTION) |
139 | | LuaStateSetTX(td->lua_ctx->luastate, txptr, tx_id); |
140 | | LuaStateSetFlow(td->lua_ctx->luastate, (Flow *)f); |
141 | | LuaStateSetStreamingBuffer(td->lua_ctx->luastate, &b); |
142 | | |
143 | | /* prepare data to pass to script */ |
144 | | lua_getglobal(td->lua_ctx->luastate, "log"); |
145 | | lua_newtable(td->lua_ctx->luastate); |
146 | | |
147 | | if (flags & OUTPUT_STREAMING_FLAG_TRANSACTION) |
148 | | LuaPushTableKeyValueInt(td->lua_ctx->luastate, "tx_id", (int)(tx_id)); |
149 | | |
150 | | int retval = lua_pcall(td->lua_ctx->luastate, 1, 0, 0); |
151 | | if (retval != 0) { |
152 | | SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1)); |
153 | | } |
154 | | |
155 | | SCMutexUnlock(&td->lua_ctx->m); |
156 | | |
157 | | SCReturnInt(TM_ECODE_OK); |
158 | | } |
159 | | |
160 | | /** \internal |
161 | | * \brief Packet Logger for lua scripts, for alerts |
162 | | * |
163 | | * A single call to this function will run one script for a single |
164 | | * packet. If it is called, it means that the registered condition |
165 | | * function has returned TRUE. |
166 | | * |
167 | | * The script is called once for each alert stored in the packet. |
168 | | * |
169 | | * NOTE: p->flow is UNlocked |
170 | | */ |
171 | | static int LuaPacketLoggerAlerts(ThreadVars *tv, void *thread_data, const Packet *p) |
172 | | { |
173 | | LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data; |
174 | | |
175 | | char timebuf[64]; |
176 | | CreateTimeString(p->ts, timebuf, sizeof(timebuf)); |
177 | | |
178 | | if (!(PKT_IS_IPV4(p)) && !(PKT_IS_IPV6(p))) { |
179 | | /* decoder event */ |
180 | | goto not_supported; |
181 | | } |
182 | | |
183 | | /* loop through alerts stored in the packet */ |
184 | | SCMutexLock(&td->lua_ctx->m); |
185 | | uint16_t cnt; |
186 | | for (cnt = 0; cnt < p->alerts.cnt; cnt++) { |
187 | | const PacketAlert *pa = &p->alerts.alerts[cnt]; |
188 | | if (unlikely(pa->s == NULL)) { |
189 | | continue; |
190 | | } |
191 | | |
192 | | lua_getglobal(td->lua_ctx->luastate, "log"); |
193 | | |
194 | | void *txptr = NULL; |
195 | | if (p->flow && p->flow->alstate && (pa->flags & PACKET_ALERT_FLAG_TX)) |
196 | | txptr = AppLayerParserGetTx( |
197 | | p->flow->proto, p->flow->alproto, p->flow->alstate, pa->tx_id); |
198 | | |
199 | | LuaStateSetThreadVars(td->lua_ctx->luastate, tv); |
200 | | LuaStateSetPacket(td->lua_ctx->luastate, (Packet *)p); |
201 | | LuaStateSetTX(td->lua_ctx->luastate, txptr, pa->tx_id); |
202 | | LuaStateSetFlow(td->lua_ctx->luastate, p->flow); |
203 | | LuaStateSetPacketAlert(td->lua_ctx->luastate, (PacketAlert *)pa); |
204 | | |
205 | | /* prepare data to pass to script */ |
206 | | //lua_newtable(td->lua_ctx->luastate); |
207 | | |
208 | | int retval = lua_pcall(td->lua_ctx->luastate, 0, 0, 0); |
209 | | if (retval != 0) { |
210 | | SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1)); |
211 | | } |
212 | | } |
213 | | SCMutexUnlock(&td->lua_ctx->m); |
214 | | not_supported: |
215 | | SCReturnInt(0); |
216 | | } |
217 | | |
218 | | static int LuaPacketConditionAlerts(ThreadVars *tv, void *data, const Packet *p) |
219 | | { |
220 | | if (p->alerts.cnt > 0) |
221 | | return TRUE; |
222 | | return FALSE; |
223 | | } |
224 | | |
225 | | /** \internal |
226 | | * \brief Packet Logger for lua scripts, for packets |
227 | | * |
228 | | * A single call to this function will run one script for a single |
229 | | * packet. If it is called, it means that the registered condition |
230 | | * function has returned TRUE. |
231 | | * |
232 | | * The script is called once for each packet. |
233 | | * |
234 | | * NOTE: p->flow is UNlocked |
235 | | */ |
236 | | static int LuaPacketLogger(ThreadVars *tv, void *thread_data, const Packet *p) |
237 | | { |
238 | | LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data; |
239 | | |
240 | | char timebuf[64]; |
241 | | |
242 | | if ((!(PKT_IS_IPV4(p))) && (!(PKT_IS_IPV6(p)))) { |
243 | | goto not_supported; |
244 | | } |
245 | | |
246 | | CreateTimeString(p->ts, timebuf, sizeof(timebuf)); |
247 | | |
248 | | /* loop through alerts stored in the packet */ |
249 | | SCMutexLock(&td->lua_ctx->m); |
250 | | lua_getglobal(td->lua_ctx->luastate, "log"); |
251 | | |
252 | | LuaStateSetThreadVars(td->lua_ctx->luastate, tv); |
253 | | LuaStateSetPacket(td->lua_ctx->luastate, (Packet *)p); |
254 | | LuaStateSetFlow(td->lua_ctx->luastate, p->flow); |
255 | | |
256 | | /* prepare data to pass to script */ |
257 | | lua_newtable(td->lua_ctx->luastate); |
258 | | |
259 | | int retval = lua_pcall(td->lua_ctx->luastate, 1, 0, 0); |
260 | | if (retval != 0) { |
261 | | SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1)); |
262 | | } |
263 | | SCMutexUnlock(&td->lua_ctx->m); |
264 | | not_supported: |
265 | | SCReturnInt(0); |
266 | | } |
267 | | |
268 | | static int LuaPacketCondition(ThreadVars *tv, void *data, const Packet *p) |
269 | | { |
270 | | return TRUE; |
271 | | } |
272 | | |
273 | | /** \internal |
274 | | * \brief File API Logger function for Lua scripts |
275 | | * |
276 | | * Executes a script once for one file. |
277 | | * |
278 | | * NOTE p->flow is locked at this point |
279 | | */ |
280 | | static int LuaFileLogger(ThreadVars *tv, void *thread_data, const Packet *p, const File *ff, |
281 | | void *tx, const uint64_t tx_id, uint8_t dir) |
282 | | { |
283 | | SCEnter(); |
284 | | LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data; |
285 | | |
286 | | if ((!(PKT_IS_IPV4(p))) && (!(PKT_IS_IPV6(p)))) |
287 | | return 0; |
288 | | |
289 | | BUG_ON(ff->flags & FILE_LOGGED); |
290 | | |
291 | | SCLogDebug("ff %p", ff); |
292 | | |
293 | | SCMutexLock(&td->lua_ctx->m); |
294 | | |
295 | | LuaStateSetThreadVars(td->lua_ctx->luastate, tv); |
296 | | LuaStateSetPacket(td->lua_ctx->luastate, (Packet *)p); |
297 | | LuaStateSetTX(td->lua_ctx->luastate, tx, tx_id); |
298 | | LuaStateSetFlow(td->lua_ctx->luastate, p->flow); |
299 | | LuaStateSetFile(td->lua_ctx->luastate, (File *)ff); |
300 | | |
301 | | /* get the lua function to call */ |
302 | | lua_getglobal(td->lua_ctx->luastate, "log"); |
303 | | |
304 | | int retval = lua_pcall(td->lua_ctx->luastate, 0, 0, 0); |
305 | | if (retval != 0) { |
306 | | SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1)); |
307 | | } |
308 | | SCMutexUnlock(&td->lua_ctx->m); |
309 | | return 0; |
310 | | } |
311 | | |
312 | | /** \internal |
313 | | * \brief Flow API Logger function for Lua scripts |
314 | | * |
315 | | * Executes a script once for one flow |
316 | | * |
317 | | * Note: flow 'f' is locked |
318 | | */ |
319 | | static int LuaFlowLogger(ThreadVars *tv, void *thread_data, Flow *f) |
320 | | { |
321 | | SCEnter(); |
322 | | LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data; |
323 | | |
324 | | SCLogDebug("f %p", f); |
325 | | |
326 | | SCMutexLock(&td->lua_ctx->m); |
327 | | |
328 | | LuaStateSetThreadVars(td->lua_ctx->luastate, tv); |
329 | | LuaStateSetFlow(td->lua_ctx->luastate, f); |
330 | | |
331 | | /* get the lua function to call */ |
332 | | lua_getglobal(td->lua_ctx->luastate, "log"); |
333 | | |
334 | | int retval = lua_pcall(td->lua_ctx->luastate, 0, 0, 0); |
335 | | if (retval != 0) { |
336 | | SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1)); |
337 | | } |
338 | | SCMutexUnlock(&td->lua_ctx->m); |
339 | | return 0; |
340 | | } |
341 | | |
342 | | |
343 | | |
344 | | static int LuaStatsLogger(ThreadVars *tv, void *thread_data, const StatsTable *st) |
345 | | { |
346 | | SCEnter(); |
347 | | LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data; |
348 | | |
349 | | SCMutexLock(&td->lua_ctx->m); |
350 | | |
351 | | lua_State *luastate = td->lua_ctx->luastate; |
352 | | /* get the lua function to call */ |
353 | | lua_getglobal(td->lua_ctx->luastate, "log"); |
354 | | |
355 | | /* create lua array, which is really just a table. The key is an int (1-x), |
356 | | * the value another table with named fields: name, tm_name, value, pvalue. |
357 | | * { 1, { name=<name>, tmname=<tm_name>, value=<value>, pvalue=<pvalue>}} |
358 | | * { 2, { name=<name>, tmname=<tm_name>, value=<value>, pvalue=<pvalue>}} |
359 | | * etc |
360 | | */ |
361 | | lua_newtable(luastate); |
362 | | uint32_t u = 0; |
363 | | for (; u < st->nstats; u++) { |
364 | | lua_pushinteger(luastate, u + 1); |
365 | | |
366 | | lua_newtable(luastate); |
367 | | |
368 | | lua_pushstring(luastate, "name"); |
369 | | lua_pushstring(luastate, st->stats[u].name); |
370 | | lua_settable(luastate, -3); |
371 | | |
372 | | lua_pushstring(luastate, "tmname"); |
373 | | lua_pushstring(luastate, st->stats[u].tm_name); |
374 | | lua_settable(luastate, -3); |
375 | | |
376 | | lua_pushstring(luastate, "value"); |
377 | | lua_pushinteger(luastate, st->stats[u].value); |
378 | | lua_settable(luastate, -3); |
379 | | |
380 | | lua_pushstring(luastate, "pvalue"); |
381 | | lua_pushinteger(luastate, st->stats[u].pvalue); |
382 | | lua_settable(luastate, -3); |
383 | | |
384 | | lua_settable(luastate, -3); |
385 | | } |
386 | | |
387 | | int retval = lua_pcall(td->lua_ctx->luastate, 1, 0, 0); |
388 | | if (retval != 0) { |
389 | | SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1)); |
390 | | } |
391 | | SCMutexUnlock(&td->lua_ctx->m); |
392 | | return 0; |
393 | | |
394 | | } |
395 | | |
396 | | typedef struct LogLuaScriptOptions_ { |
397 | | AppProto alproto; |
398 | | int packet; |
399 | | int alerts; |
400 | | int file; |
401 | | int streaming; |
402 | | int tcp_data; |
403 | | int http_body; |
404 | | int flow; |
405 | | int stats; |
406 | | } LogLuaScriptOptions; |
407 | | |
408 | | /** \brief load and evaluate the script |
409 | | * |
410 | | * This function parses the script, checks if all the required functions |
411 | | * are defined and runs the 'init' function. The init function will inform |
412 | | * us what the scripts needs are. |
413 | | * |
414 | | * \param filename filename of lua script file |
415 | | * \param options struct to pass script requirements/options back to caller |
416 | | * \retval errcode 0 ok, -1 error |
417 | | */ |
418 | | static int LuaScriptInit(const char *filename, LogLuaScriptOptions *options) { |
419 | | lua_State *luastate = LuaGetState(); |
420 | | if (luastate == NULL) |
421 | | goto error; |
422 | | luaL_openlibs(luastate); |
423 | | |
424 | | int status = luaL_loadfile(luastate, filename); |
425 | | if (status) { |
426 | | SCLogError("couldn't load file: %s", lua_tostring(luastate, -1)); |
427 | | goto error; |
428 | | } |
429 | | |
430 | | /* prime the script (or something) */ |
431 | | if (lua_pcall(luastate, 0, 0, 0) != 0) { |
432 | | SCLogError("couldn't prime file: %s", lua_tostring(luastate, -1)); |
433 | | goto error; |
434 | | } |
435 | | |
436 | | lua_getglobal(luastate, "init"); |
437 | | if (lua_type(luastate, -1) != LUA_TFUNCTION) { |
438 | | SCLogError("no init function in script"); |
439 | | goto error; |
440 | | } |
441 | | |
442 | | lua_newtable(luastate); /* stack at -1 */ |
443 | | if (lua_gettop(luastate) == 0 || lua_type(luastate, 2) != LUA_TTABLE) { |
444 | | SCLogError("no table setup"); |
445 | | goto error; |
446 | | } |
447 | | |
448 | | lua_pushliteral(luastate, "script_api_ver"); |
449 | | lua_pushnumber (luastate, 1); |
450 | | lua_settable(luastate, -3); |
451 | | |
452 | | if (lua_pcall(luastate, 1, 1, 0) != 0) { |
453 | | SCLogError("couldn't run script 'init' function: %s", lua_tostring(luastate, -1)); |
454 | | goto error; |
455 | | } |
456 | | |
457 | | /* process returns from script */ |
458 | | if (lua_gettop(luastate) == 0) { |
459 | | SCLogError("init function in script should return table, nothing returned"); |
460 | | goto error; |
461 | | } |
462 | | if (lua_type(luastate, 1) != LUA_TTABLE) { |
463 | | SCLogError("init function in script should return table, returned is not table"); |
464 | | goto error; |
465 | | } |
466 | | |
467 | | lua_pushnil(luastate); |
468 | | const char *k, *v; |
469 | | while (lua_next(luastate, -2)) { |
470 | | k = lua_tostring(luastate, -2); |
471 | | if (k == NULL) |
472 | | continue; |
473 | | |
474 | | v = lua_tostring(luastate, -1); |
475 | | lua_pop(luastate, 1); |
476 | | if (v == NULL) |
477 | | continue; |
478 | | |
479 | | SCLogDebug("k='%s', v='%s'", k, v); |
480 | | |
481 | | if (strcmp(k,"protocol") == 0 && strcmp(v, "http") == 0) |
482 | | options->alproto = ALPROTO_HTTP1; |
483 | | else if (strcmp(k,"protocol") == 0 && strcmp(v, "dns") == 0) |
484 | | options->alproto = ALPROTO_DNS; |
485 | | else if (strcmp(k,"protocol") == 0 && strcmp(v, "tls") == 0) |
486 | | options->alproto = ALPROTO_TLS; |
487 | | else if (strcmp(k,"protocol") == 0 && strcmp(v, "ssh") == 0) |
488 | | options->alproto = ALPROTO_SSH; |
489 | | else if (strcmp(k,"protocol") == 0 && strcmp(v, "smtp") == 0) |
490 | | options->alproto = ALPROTO_SMTP; |
491 | | else if (strcmp(k, "type") == 0 && strcmp(v, "packet") == 0) |
492 | | options->packet = 1; |
493 | | else if (strcmp(k, "filter") == 0 && strcmp(v, "alerts") == 0) |
494 | | options->alerts = 1; |
495 | | else if (strcmp(k, "type") == 0 && strcmp(v, "file") == 0) |
496 | | options->file = 1; |
497 | | else if (strcmp(k, "type") == 0 && strcmp(v, "streaming") == 0) |
498 | | options->streaming = 1; |
499 | | else if (strcmp(k, "type") == 0 && strcmp(v, "flow") == 0) |
500 | | options->flow = 1; |
501 | | else if (strcmp(k, "filter") == 0 && strcmp(v, "tcp") == 0) |
502 | | options->tcp_data = 1; |
503 | | else if (strcmp(k, "type") == 0 && strcmp(v, "stats") == 0) |
504 | | options->stats = 1; |
505 | | else { |
506 | | SCLogError("unknown key and/or value: k='%s', v='%s'", k, v); |
507 | | goto error; |
508 | | } |
509 | | } |
510 | | |
511 | | if (((options->alproto != ALPROTO_UNKNOWN)) + options->packet + options->file > 1) { |
512 | | SCLogError("invalid combination of 'needs' in the script"); |
513 | | goto error; |
514 | | } |
515 | | |
516 | | lua_getglobal(luastate, "setup"); |
517 | | if (lua_type(luastate, -1) != LUA_TFUNCTION) { |
518 | | SCLogError("no setup function in script"); |
519 | | goto error; |
520 | | } |
521 | | |
522 | | lua_getglobal(luastate, "log"); |
523 | | if (lua_type(luastate, -1) != LUA_TFUNCTION) { |
524 | | SCLogError("no log function in script"); |
525 | | goto error; |
526 | | } |
527 | | |
528 | | lua_getglobal(luastate, "deinit"); |
529 | | if (lua_type(luastate, -1) != LUA_TFUNCTION) { |
530 | | SCLogError("no deinit function in script"); |
531 | | goto error; |
532 | | } |
533 | | |
534 | | LuaReturnState(luastate); |
535 | | return 0; |
536 | | error: |
537 | | if (luastate) |
538 | | LuaReturnState(luastate); |
539 | | return -1; |
540 | | } |
541 | | |
542 | | /** \brief setup a luastate for use at runtime |
543 | | * |
544 | | * This loads the script, primes it and then runs the 'setup' function. |
545 | | * |
546 | | * \retval state Returns the set up luastate on success, NULL on error |
547 | | */ |
548 | | static lua_State *LuaScriptSetup(const char *filename) |
549 | | { |
550 | | lua_State *luastate = LuaGetState(); |
551 | | if (luastate == NULL) { |
552 | | SCLogError("luaL_newstate failed"); |
553 | | goto error; |
554 | | } |
555 | | |
556 | | luaL_openlibs(luastate); |
557 | | |
558 | | int status = luaL_loadfile(luastate, filename); |
559 | | if (status) { |
560 | | SCLogError("couldn't load file: %s", lua_tostring(luastate, -1)); |
561 | | goto error; |
562 | | } |
563 | | |
564 | | /* prime the script */ |
565 | | if (lua_pcall(luastate, 0, 0, 0) != 0) { |
566 | | SCLogError("couldn't prime file: %s", lua_tostring(luastate, -1)); |
567 | | goto error; |
568 | | } |
569 | | |
570 | | lua_getglobal(luastate, "setup"); |
571 | | |
572 | | /* register functions common to all */ |
573 | | LuaRegisterFunctions(luastate); |
574 | | /* unconditionally register http function. They will only work |
575 | | * if the tx is registered in the state at runtime though. */ |
576 | | LuaRegisterHttpFunctions(luastate); |
577 | | LuaRegisterDnsFunctions(luastate); |
578 | | LuaRegisterJa3Functions(luastate); |
579 | | LuaRegisterTlsFunctions(luastate); |
580 | | LuaRegisterSshFunctions(luastate); |
581 | | LuaRegisterHasshFunctions(luastate); |
582 | | LuaRegisterSmtpFunctions(luastate); |
583 | | |
584 | | if (lua_pcall(luastate, 0, 0, 0) != 0) { |
585 | | SCLogError("couldn't run script 'setup' function: %s", lua_tostring(luastate, -1)); |
586 | | goto error; |
587 | | } |
588 | | |
589 | | SCLogDebug("lua_State %p is set up", luastate); |
590 | | return luastate; |
591 | | error: |
592 | | if (luastate) |
593 | | LuaReturnState(luastate); |
594 | | return NULL; |
595 | | } |
596 | | |
597 | | static void LogLuaSubFree(OutputCtx *oc) { |
598 | | if (oc->data) |
599 | | SCFree(oc->data); |
600 | | SCFree(oc); |
601 | | } |
602 | | |
603 | | /** \brief initialize output for a script instance |
604 | | * |
605 | | * Runs script 'setup' function. |
606 | | */ |
607 | | static OutputInitResult OutputLuaLogInitSub(ConfNode *conf, OutputCtx *parent_ctx) |
608 | | { |
609 | | OutputInitResult result = { NULL, false }; |
610 | | if (conf == NULL) |
611 | | return result; |
612 | | |
613 | | LogLuaCtx *lua_ctx = SCMalloc(sizeof(LogLuaCtx)); |
614 | | if (unlikely(lua_ctx == NULL)) |
615 | | return result; |
616 | | memset(lua_ctx, 0x00, sizeof(*lua_ctx)); |
617 | | |
618 | | OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx)); |
619 | | if (unlikely(output_ctx == NULL)) { |
620 | | SCFree(lua_ctx); |
621 | | return result; |
622 | | } |
623 | | |
624 | | SCMutexInit(&lua_ctx->m, NULL); |
625 | | |
626 | | const char *dir = ""; |
627 | | if (parent_ctx && parent_ctx->data) { |
628 | | LogLuaMasterCtx *mc = parent_ctx->data; |
629 | | dir = mc->path; |
630 | | } |
631 | | |
632 | | char path[PATH_MAX] = ""; |
633 | | int ret = snprintf(path, sizeof(path),"%s%s%s", dir, strlen(dir) ? "/" : "", conf->val); |
634 | | if (ret < 0 || ret == sizeof(path)) { |
635 | | SCLogError("failed to construct lua script path"); |
636 | | goto error; |
637 | | } |
638 | | SCLogDebug("script full path %s", path); |
639 | | |
640 | | SCMutexLock(&lua_ctx->m); |
641 | | lua_ctx->luastate = LuaScriptSetup(path); |
642 | | SCMutexUnlock(&lua_ctx->m); |
643 | | if (lua_ctx->luastate == NULL) |
644 | | goto error; |
645 | | |
646 | | SCLogDebug("lua_ctx %p", lua_ctx); |
647 | | |
648 | | output_ctx->data = lua_ctx; |
649 | | output_ctx->DeInit = LogLuaSubFree; |
650 | | |
651 | | result.ctx = output_ctx; |
652 | | result.ok = true; |
653 | | return result; |
654 | | error: |
655 | | SCMutexDestroy(&lua_ctx->m); |
656 | | SCFree(lua_ctx); |
657 | | SCFree(output_ctx); |
658 | | return result; |
659 | | } |
660 | | |
661 | | static void LogLuaMasterFree(OutputCtx *oc) |
662 | | { |
663 | | if (oc->data) |
664 | | SCFree(oc->data); |
665 | | |
666 | | OutputModule *om, *tom; |
667 | | TAILQ_FOREACH_SAFE(om, &oc->submodules, entries, tom) { |
668 | | SCFree(om); |
669 | | } |
670 | | SCFree(oc); |
671 | | } |
672 | | |
673 | | /** \internal |
674 | | * \brief initialize output instance for lua module |
675 | | * |
676 | | * Parses nested script list, primes them to find out what they |
677 | | * inspect, then fills the OutputCtx::submodules list with the |
678 | | * proper Logger function for the data type the script needs. |
679 | | */ |
680 | | static OutputInitResult OutputLuaLogInit(ConfNode *conf) |
681 | | { |
682 | | OutputInitResult result = { NULL, false }; |
683 | | const char *dir = ConfNodeLookupChildValue(conf, "scripts-dir"); |
684 | | if (dir == NULL) |
685 | | dir = ""; |
686 | | |
687 | | ConfNode *scripts = ConfNodeLookupChild(conf, "scripts"); |
688 | | if (scripts == NULL) { |
689 | | /* No "outputs" section in the configuration. */ |
690 | | SCLogInfo("scripts not defined"); |
691 | | return result; |
692 | | } |
693 | | |
694 | | /* global output ctx setup */ |
695 | | OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx)); |
696 | | if (unlikely(output_ctx == NULL)) { |
697 | | return result; |
698 | | } |
699 | | output_ctx->DeInit = LogLuaMasterFree; |
700 | | output_ctx->data = SCCalloc(1, sizeof(LogLuaMasterCtx)); |
701 | | if (unlikely(output_ctx->data == NULL)) { |
702 | | SCFree(output_ctx); |
703 | | return result; |
704 | | } |
705 | | LogLuaMasterCtx *master_config = output_ctx->data; |
706 | | strlcpy(master_config->path, dir, sizeof(master_config->path)); |
707 | | TAILQ_INIT(&output_ctx->submodules); |
708 | | |
709 | | /* check the enables scripts and set them up as submodules */ |
710 | | ConfNode *script; |
711 | | TAILQ_FOREACH(script, &scripts->head, next) { |
712 | | SCLogInfo("enabling script %s", script->val); |
713 | | LogLuaScriptOptions opts; |
714 | | memset(&opts, 0x00, sizeof(opts)); |
715 | | |
716 | | char path[PATH_MAX] = ""; |
717 | | snprintf(path, sizeof(path),"%s%s%s", dir, strlen(dir) ? "/" : "", script->val); |
718 | | SCLogDebug("script full path %s", path); |
719 | | |
720 | | int r = LuaScriptInit(path, &opts); |
721 | | if (r != 0) { |
722 | | SCLogError("couldn't initialize script"); |
723 | | goto error; |
724 | | } |
725 | | |
726 | | /* create an OutputModule for this script, based |
727 | | * on it's needs. */ |
728 | | OutputModule *om = SCCalloc(1, sizeof(*om)); |
729 | | if (om == NULL) { |
730 | | SCLogError("calloc() failed"); |
731 | | goto error; |
732 | | } |
733 | | |
734 | | om->name = MODULE_NAME; |
735 | | om->conf_name = script->val; |
736 | | om->InitSubFunc = OutputLuaLogInitSub; |
737 | | om->ThreadInit = LuaLogThreadInit; |
738 | | om->ThreadDeinit = LuaLogThreadDeinit; |
739 | | |
740 | | if (opts.alproto == ALPROTO_HTTP1 && opts.streaming) { |
741 | | om->StreamingLogFunc = LuaStreamingLogger; |
742 | | om->stream_type = STREAMING_HTTP_BODIES; |
743 | | om->alproto = ALPROTO_HTTP1; |
744 | | AppLayerHtpEnableRequestBodyCallback(); |
745 | | AppLayerHtpEnableResponseBodyCallback(); |
746 | | } else if (opts.alproto == ALPROTO_HTTP1) { |
747 | | om->TxLogFunc = LuaTxLogger; |
748 | | om->alproto = ALPROTO_HTTP1; |
749 | | om->ts_log_progress = -1; |
750 | | om->tc_log_progress = -1; |
751 | | AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_HTTP1); |
752 | | } else if (opts.alproto == ALPROTO_TLS) { |
753 | | om->TxLogFunc = LuaTxLogger; |
754 | | om->alproto = ALPROTO_TLS; |
755 | | om->tc_log_progress = TLS_HANDSHAKE_DONE; |
756 | | om->ts_log_progress = TLS_HANDSHAKE_DONE; |
757 | | AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_TLS); |
758 | | } else if (opts.alproto == ALPROTO_DNS) { |
759 | | om->TxLogFunc = LuaTxLogger; |
760 | | om->alproto = ALPROTO_DNS; |
761 | | om->ts_log_progress = -1; |
762 | | om->tc_log_progress = -1; |
763 | | AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_DNS); |
764 | | AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_DNS); |
765 | | } else if (opts.alproto == ALPROTO_SSH) { |
766 | | om->TxLogFunc = LuaTxLogger; |
767 | | om->alproto = ALPROTO_SSH; |
768 | | om->TxLogCondition = SSHTxLogCondition; |
769 | | AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_SSH); |
770 | | } else if (opts.alproto == ALPROTO_SMTP) { |
771 | | om->TxLogFunc = LuaTxLogger; |
772 | | om->alproto = ALPROTO_SMTP; |
773 | | om->ts_log_progress = -1; |
774 | | om->tc_log_progress = -1; |
775 | | AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_SMTP); |
776 | | } else if (opts.packet && opts.alerts) { |
777 | | om->PacketLogFunc = LuaPacketLoggerAlerts; |
778 | | om->PacketConditionFunc = LuaPacketConditionAlerts; |
779 | | } else if (opts.packet && opts.alerts == 0) { |
780 | | om->PacketLogFunc = LuaPacketLogger; |
781 | | om->PacketConditionFunc = LuaPacketCondition; |
782 | | } else if (opts.file) { |
783 | | om->FileLogFunc = LuaFileLogger; |
784 | | AppLayerHtpNeedFileInspection(); |
785 | | } else if (opts.streaming && opts.tcp_data) { |
786 | | om->StreamingLogFunc = LuaStreamingLogger; |
787 | | om->stream_type = STREAMING_TCP_DATA; |
788 | | } else if (opts.flow) { |
789 | | om->FlowLogFunc = LuaFlowLogger; |
790 | | } else if (opts.stats) { |
791 | | om->StatsLogFunc = LuaStatsLogger; |
792 | | } else { |
793 | | SCLogError("failed to setup thread module"); |
794 | | SCFree(om); |
795 | | goto error; |
796 | | } |
797 | | |
798 | | TAILQ_INSERT_TAIL(&output_ctx->submodules, om, entries); |
799 | | } |
800 | | |
801 | | result.ctx = output_ctx; |
802 | | result.ok = true; |
803 | | return result; |
804 | | |
805 | | error: |
806 | | if (output_ctx->DeInit) |
807 | | output_ctx->DeInit(output_ctx); |
808 | | |
809 | | int failure_fatal = 0; |
810 | | if (ConfGetBool("engine.init-failure-fatal", &failure_fatal) != 1) { |
811 | | SCLogDebug("ConfGetBool could not load the value."); |
812 | | } |
813 | | if (failure_fatal) { |
814 | | FatalError("Error during setup of lua output. Details should be " |
815 | | "described in previous error messages. Shutting down..."); |
816 | | } |
817 | | |
818 | | return result; |
819 | | } |
820 | | |
821 | | /** \internal |
822 | | * \brief Run the scripts 'deinit' function |
823 | | */ |
824 | | static void OutputLuaLogDoDeinit(LogLuaCtx *lua_ctx) |
825 | | { |
826 | | lua_State *luastate = lua_ctx->luastate; |
827 | | |
828 | | lua_getglobal(luastate, "deinit"); |
829 | | if (lua_type(luastate, -1) != LUA_TFUNCTION) { |
830 | | SCLogError("no deinit function in script"); |
831 | | return; |
832 | | } |
833 | | //LuaPrintStack(luastate); |
834 | | |
835 | | if (lua_pcall(luastate, 0, 0, 0) != 0) { |
836 | | SCLogError("couldn't run script 'deinit' function: %s", lua_tostring(luastate, -1)); |
837 | | return; |
838 | | } |
839 | | LuaReturnState(luastate); |
840 | | } |
841 | | |
842 | | /** \internal |
843 | | * \brief Initialize the thread storage for lua |
844 | | * |
845 | | * Currently only stores a pointer to the global LogLuaCtx |
846 | | */ |
847 | | static TmEcode LuaLogThreadInit(ThreadVars *t, const void *initdata, void **data) |
848 | | { |
849 | | LogLuaThreadCtx *td = SCMalloc(sizeof(*td)); |
850 | | if (unlikely(td == NULL)) |
851 | | return TM_ECODE_FAILED; |
852 | | memset(td, 0, sizeof(*td)); |
853 | | |
854 | | if (initdata == NULL) { |
855 | | SCLogDebug("Error getting context for LuaLog. \"initdata\" argument NULL"); |
856 | | SCFree(td); |
857 | | return TM_ECODE_FAILED; |
858 | | } |
859 | | |
860 | | LogLuaCtx *lua_ctx = ((OutputCtx *)initdata)->data; |
861 | | SCLogDebug("lua_ctx %p", lua_ctx); |
862 | | td->lua_ctx = lua_ctx; |
863 | | *data = (void *)td; |
864 | | return TM_ECODE_OK; |
865 | | } |
866 | | |
867 | | /** \internal |
868 | | * \brief Deinit the thread storage for lua |
869 | | * |
870 | | * Calls OutputLuaLogDoDeinit if no-one else already did. |
871 | | */ |
872 | | static TmEcode LuaLogThreadDeinit(ThreadVars *t, void *data) |
873 | | { |
874 | | LogLuaThreadCtx *td = (LogLuaThreadCtx *)data; |
875 | | if (td == NULL) { |
876 | | return TM_ECODE_OK; |
877 | | } |
878 | | |
879 | | SCMutexLock(&td->lua_ctx->m); |
880 | | if (td->lua_ctx->deinit_once == 0) { |
881 | | OutputLuaLogDoDeinit(td->lua_ctx); |
882 | | td->lua_ctx->deinit_once = 1; |
883 | | } |
884 | | SCMutexUnlock(&td->lua_ctx->m); |
885 | | |
886 | | /* clear memory */ |
887 | | memset(td, 0, sizeof(*td)); |
888 | | |
889 | | SCFree(td); |
890 | | return TM_ECODE_OK; |
891 | | } |
892 | | |
893 | | void LuaLogRegister(void) { |
894 | | /* register as separate module */ |
895 | | OutputRegisterModule(MODULE_NAME, "lua", OutputLuaLogInit); |
896 | | } |
897 | | |
898 | | #else /* HAVE_LUA */ |
899 | | |
900 | 71 | void LuaLogRegister (void) { |
901 | | /* no-op */ |
902 | 71 | } |
903 | | |
904 | | #endif /* HAVE_LUA */ |