/src/suricata8/src/util-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 | | * Common function for Lua |
24 | | */ |
25 | | |
26 | | #include "suricata-common.h" |
27 | | #include "detect.h" |
28 | | #include "pkt-var.h" |
29 | | #include "conf.h" |
30 | | |
31 | | #include "threads.h" |
32 | | #include "threadvars.h" |
33 | | #include "tm-threads.h" |
34 | | |
35 | | #include "util-print.h" |
36 | | #include "util-unittest.h" |
37 | | |
38 | | #include "util-debug.h" |
39 | | |
40 | | #include "output.h" |
41 | | #include "app-layer-htp.h" |
42 | | #include "app-layer.h" |
43 | | #include "app-layer-parser.h" |
44 | | #include "util-privs.h" |
45 | | #include "util-buffer.h" |
46 | | #include "util-proto-name.h" |
47 | | #include "util-logopenfile.h" |
48 | | #include "util-time.h" |
49 | | |
50 | | #include "lua.h" |
51 | | #include "lualib.h" |
52 | | #include "lauxlib.h" |
53 | | |
54 | | #include "util-lua.h" |
55 | | #include "util-lua-sandbox.h" |
56 | | #include "detect-lua-extensions.h" |
57 | | |
58 | | lua_State *LuaGetState(void) |
59 | 0 | { |
60 | 0 | lua_State *s = NULL; |
61 | 0 | s = luaL_newstate(); |
62 | 0 | return s; |
63 | 0 | } |
64 | | |
65 | | void LuaReturnState(lua_State *s) |
66 | 0 | { |
67 | 0 | if (s != NULL) { |
68 | | /* clear the stack */ |
69 | 0 | while (lua_gettop(s) > 0) { |
70 | 0 | lua_pop(s, 1); |
71 | 0 | } |
72 | 0 | lua_close(s); |
73 | 0 | } |
74 | 0 | } |
75 | | |
76 | | /* key for tv (threadvars) pointer */ |
77 | | const char lua_ext_key_tv[] = "suricata:lua:tv:ptr"; |
78 | | /* key for the invocation-generation counter pointer */ |
79 | | const char lua_ext_key_invocation_gen[] = "suricata:lua:invocation:gen"; |
80 | | /* key for tx pointer */ |
81 | | const char lua_ext_key_tx[] = "suricata:lua:tx:ptr"; |
82 | | /* key for tx id */ |
83 | | const char lua_ext_key_tx_id[] = "suricata:lua:tx_id"; |
84 | | /* key for p (packet) pointer */ |
85 | | const char lua_ext_key_p[] = "suricata:lua:pkt:ptr"; |
86 | | /* key for f (flow) pointer */ |
87 | | const char lua_ext_key_flow[] = "suricata:lua:flow:ptr"; |
88 | | /* key for direction */ |
89 | | const char lua_ext_key_direction[] = "suricata:lua:direction"; |
90 | | |
91 | | /* key for pa (packet alert) pointer */ |
92 | | const char lua_ext_key_pa[] = "suricata:lua:pkt:alert:ptr"; |
93 | | /* key for s (signature) pointer */ |
94 | | const char lua_ext_key_s[] = "suricata:lua:signature:ptr"; |
95 | | /* key for file pointer */ |
96 | | const char lua_ext_key_file[] = "suricata:lua:file:ptr"; |
97 | | /* key for DetectEngineThreadCtx pointer */ |
98 | | const char lua_ext_key_det_ctx[] = "suricata:lua:det_ctx:ptr"; |
99 | | |
100 | | /* |
101 | | * Registry context values are wrapped in a typed userdata instead of |
102 | | * being stored as bare lightuserdata. In unsandboxed states the registry |
103 | | * is script-writable, so a bare pointer can be moved between keys and |
104 | | * re-cast by the libs (type confusion/UAF). The type id is checked on |
105 | | * every read; a retyped value fails the check and yields NULL. |
106 | | */ |
107 | | typedef struct SCLuaRegValue_ { |
108 | | uint32_t type_id; |
109 | | void *ptr; |
110 | | } SCLuaRegValue; |
111 | | |
112 | | enum { |
113 | | SCLUA_REG_INVALID = 0, |
114 | | SCLUA_REG_TV, |
115 | | SCLUA_REG_PACKET, |
116 | | SCLUA_REG_TX, |
117 | | SCLUA_REG_FLOW, |
118 | | SCLUA_REG_PACKET_ALERT, |
119 | | SCLUA_REG_SIGNATURE, |
120 | | SCLUA_REG_FILE, |
121 | | SCLUA_REG_DET_CTX, |
122 | | SCLUA_REG_DETECT_LUA_DATA, |
123 | | SCLUA_REG_INVOCATION_GEN, |
124 | | }; |
125 | | |
126 | | static void LuaStateSetReg(lua_State *luastate, const void *key, uint32_t type_id, void *ptr) |
127 | 34.0k | { |
128 | 34.0k | lua_pushlightuserdata(luastate, (void *)key); |
129 | 34.0k | if (ptr == NULL) { |
130 | | /* clear without allocating (this runs at state teardown, where the |
131 | | * sandbox allocator may be over its cap): NULL a typed slot in |
132 | | * place, drop anything else (a nil settable never inserts) */ |
133 | 0 | lua_gettable(luastate, LUA_REGISTRYINDEX); |
134 | 0 | if (lua_type(luastate, -1) == LUA_TUSERDATA && |
135 | 0 | lua_rawlen(luastate, -1) == sizeof(SCLuaRegValue)) { |
136 | 0 | SCLuaRegValue *v = (SCLuaRegValue *)lua_touserdata(luastate, -1); |
137 | 0 | if (v != NULL && v->type_id == type_id) { |
138 | 0 | v->ptr = NULL; |
139 | 0 | lua_pop(luastate, 1); |
140 | 0 | return; |
141 | 0 | } |
142 | 0 | } |
143 | 0 | lua_pop(luastate, 1); |
144 | 0 | lua_pushlightuserdata(luastate, (void *)key); |
145 | 0 | lua_pushnil(luastate); |
146 | 0 | lua_settable(luastate, LUA_REGISTRYINDEX); |
147 | 0 | return; |
148 | 0 | } |
149 | | /* the registry survives across match calls: when the slot already |
150 | | * holds a value of this type, update it in place instead of paying |
151 | | * a fresh GC allocation on every set (hot path) */ |
152 | 34.0k | lua_gettable(luastate, LUA_REGISTRYINDEX); |
153 | 34.0k | if (lua_type(luastate, -1) == LUA_TUSERDATA && |
154 | 0 | lua_rawlen(luastate, -1) == sizeof(SCLuaRegValue)) { |
155 | 0 | SCLuaRegValue *v = (SCLuaRegValue *)lua_touserdata(luastate, -1); |
156 | 0 | if (v != NULL && v->type_id == type_id) { |
157 | 0 | v->ptr = ptr; |
158 | 0 | lua_pop(luastate, 1); |
159 | 0 | return; |
160 | 0 | } |
161 | 0 | } |
162 | 34.0k | lua_pop(luastate, 1); |
163 | 34.0k | lua_pushlightuserdata(luastate, (void *)key); |
164 | 34.0k | SCLuaRegValue *v = (SCLuaRegValue *)lua_newuserdata(luastate, sizeof(*v)); |
165 | 34.0k | v->type_id = type_id; |
166 | 34.0k | v->ptr = ptr; |
167 | 34.0k | lua_settable(luastate, LUA_REGISTRYINDEX); |
168 | 34.0k | } |
169 | | |
170 | | static void *LuaStateGetReg(lua_State *luastate, const void *key, uint32_t type_id) |
171 | 0 | { |
172 | 0 | lua_pushlightuserdata(luastate, (void *)key); |
173 | 0 | lua_gettable(luastate, LUA_REGISTRYINDEX); |
174 | 0 | void *ptr = NULL; |
175 | | /* require the exact full-userdata shape before casting: a |
176 | | * lightuserdata or a foreign small userdata would otherwise be |
177 | | * re-read as SCLuaRegValue (OOB ptr on a size match) */ |
178 | 0 | if (lua_type(luastate, -1) == LUA_TUSERDATA && |
179 | 0 | lua_rawlen(luastate, -1) == sizeof(SCLuaRegValue)) { |
180 | 0 | SCLuaRegValue *v = (SCLuaRegValue *)lua_touserdata(luastate, -1); |
181 | 0 | if (v != NULL && v->type_id == type_id) { |
182 | 0 | ptr = v->ptr; |
183 | 0 | } |
184 | 0 | } |
185 | 0 | lua_pop(luastate, 1); |
186 | 0 | return ptr; |
187 | 0 | } |
188 | | |
189 | | /** \brief get tv pointer from the lua state */ |
190 | | ThreadVars *LuaStateGetThreadVars(lua_State *luastate) |
191 | 0 | { |
192 | 0 | return (ThreadVars *)LuaStateGetReg(luastate, lua_ext_key_tv, SCLUA_REG_TV); |
193 | 0 | } |
194 | | |
195 | | void LuaStateSetThreadVars(lua_State *luastate, ThreadVars *tv) |
196 | 0 | { |
197 | | /* every registry context write is one script invocation: the state's |
198 | | * invocation generation counts them, so a handle caching |
199 | | * invocation-local pointers can compare it as a generation (a tx |
200 | | * cannot be freed while a script call runs) */ |
201 | 0 | uint64_t *gen = LuaStateGetReg(luastate, lua_ext_key_invocation_gen, SCLUA_REG_INVOCATION_GEN); |
202 | 0 | if (gen != NULL) { |
203 | 0 | *gen += 1; |
204 | 0 | } |
205 | 0 | LuaStateSetReg(luastate, lua_ext_key_tv, SCLUA_REG_TV, (void *)tv); |
206 | 0 | } |
207 | | |
208 | | /* publish the state's invocation counter: the slot holds the counter's |
209 | | * address, not the counter - the value stays C-owned (sandbox context |
210 | | * or output ctx), so a script cannot forge a generation; a swapped or |
211 | | * cleared slot reads as 0 and no cursor is ever trusted */ |
212 | | void LuaStateSetInvocationGen(lua_State *luastate, uint64_t *gen) |
213 | 0 | { |
214 | 0 | LuaStateSetReg(luastate, lua_ext_key_invocation_gen, SCLUA_REG_INVOCATION_GEN, (void *)gen); |
215 | 0 | } |
216 | | |
217 | | /** \brief get the current invocation generation for this state */ |
218 | | uint64_t LuaStateGetInvocationGen(lua_State *luastate) |
219 | 0 | { |
220 | 0 | uint64_t *gen = LuaStateGetReg(luastate, lua_ext_key_invocation_gen, SCLUA_REG_INVOCATION_GEN); |
221 | 0 | return (gen != NULL) ? *gen : 0; |
222 | 0 | } |
223 | | |
224 | | /** \brief get packet pointer from the lua state */ |
225 | | Packet *LuaStateGetPacket(lua_State *luastate) |
226 | 0 | { |
227 | 0 | return (Packet *)LuaStateGetReg(luastate, lua_ext_key_p, SCLUA_REG_PACKET); |
228 | 0 | } |
229 | | |
230 | | void LuaStateSetPacket(lua_State *luastate, Packet *p) |
231 | 0 | { |
232 | 0 | LuaStateSetReg(luastate, lua_ext_key_p, SCLUA_REG_PACKET, (void *)p); |
233 | 0 | } |
234 | | |
235 | | /** \brief get tx pointer from the lua state */ |
236 | | void *LuaStateGetTX(lua_State *luastate) |
237 | 0 | { |
238 | 0 | return LuaStateGetReg(luastate, lua_ext_key_tx, SCLUA_REG_TX); |
239 | 0 | } |
240 | | |
241 | | /** \brief get tx id from the lua state */ |
242 | | uint64_t LuaStateGetTxId(lua_State *luastate) |
243 | 0 | { |
244 | 0 | lua_pushlightuserdata(luastate, (void *)&lua_ext_key_tx_id); |
245 | 0 | lua_gettable(luastate, LUA_REGISTRYINDEX); |
246 | 0 | uint64_t tx_id = lua_tointeger(luastate, -1); |
247 | 0 | lua_pop(luastate, 1); |
248 | 0 | return tx_id; |
249 | 0 | } |
250 | | void LuaStateSetTX(lua_State *luastate, void *txptr, const uint64_t tx_id) |
251 | 0 | { |
252 | 0 | LuaStateSetReg(luastate, lua_ext_key_tx, SCLUA_REG_TX, txptr); |
253 | 0 | if (txptr == NULL) { |
254 | 0 | return; /* cleared: the id is read together with the slot */ |
255 | 0 | } |
256 | | |
257 | 0 | lua_pushlightuserdata(luastate, (void *)&lua_ext_key_tx_id); |
258 | 0 | lua_pushinteger(luastate, tx_id); |
259 | 0 | lua_settable(luastate, LUA_REGISTRYINDEX); |
260 | 0 | } |
261 | | |
262 | | Flow *LuaStateGetFlow(lua_State *luastate) |
263 | 0 | { |
264 | 0 | Flow *f = (Flow *)LuaStateGetReg(luastate, lua_ext_key_flow, SCLUA_REG_FLOW); |
265 | |
|
266 | 0 | return f; |
267 | 0 | } |
268 | | |
269 | | void LuaStateSetFlow(lua_State *luastate, Flow *f) |
270 | 0 | { |
271 | | /* flow */ |
272 | 0 | LuaStateSetReg(luastate, lua_ext_key_flow, SCLUA_REG_FLOW, (void *)f); |
273 | 0 | } |
274 | | |
275 | | /** \brief get packet alert pointer from the lua state */ |
276 | | PacketAlert *LuaStateGetPacketAlert(lua_State *luastate) |
277 | 0 | { |
278 | 0 | return (PacketAlert *)LuaStateGetReg(luastate, lua_ext_key_pa, SCLUA_REG_PACKET_ALERT); |
279 | 0 | } |
280 | | |
281 | | void LuaStateSetPacketAlert(lua_State *luastate, PacketAlert *pa) |
282 | 0 | { |
283 | 0 | LuaStateSetReg(luastate, lua_ext_key_pa, SCLUA_REG_PACKET_ALERT, (void *)pa); |
284 | 0 | } |
285 | | |
286 | | /** \brief get signature pointer from the lua state */ |
287 | | Signature *LuaStateGetSignature(lua_State *luastate) |
288 | 0 | { |
289 | 0 | return (Signature *)LuaStateGetReg(luastate, lua_ext_key_s, SCLUA_REG_SIGNATURE); |
290 | 0 | } |
291 | | |
292 | | void LuaStateSetSignature(lua_State *luastate, const Signature *s) |
293 | 0 | { |
294 | 0 | LuaStateSetReg(luastate, lua_ext_key_s, SCLUA_REG_SIGNATURE, (void *)s); |
295 | 0 | } |
296 | | |
297 | | /** \brief get file pointer from the lua state */ |
298 | | File *LuaStateGetFile(lua_State *luastate) |
299 | 0 | { |
300 | 0 | return (File *)LuaStateGetReg(luastate, lua_ext_key_file, SCLUA_REG_FILE); |
301 | 0 | } |
302 | | |
303 | | void LuaStateSetFile(lua_State *luastate, File *file) |
304 | 0 | { |
305 | 0 | LuaStateSetReg(luastate, lua_ext_key_file, SCLUA_REG_FILE, (void *)file); |
306 | 0 | } |
307 | | |
308 | | /** \brief get DetectEngineThreadCtx pointer from the lua state */ |
309 | | DetectEngineThreadCtx *LuaStateGetDetCtx(lua_State *luastate) |
310 | 0 | { |
311 | 0 | return (DetectEngineThreadCtx *)LuaStateGetReg( |
312 | 0 | luastate, lua_ext_key_det_ctx, SCLUA_REG_DET_CTX); |
313 | 0 | } |
314 | | |
315 | | void LuaStateSetDetCtx(lua_State *luastate, DetectEngineThreadCtx *det_ctx) |
316 | 0 | { |
317 | 0 | LuaStateSetReg(luastate, lua_ext_key_det_ctx, SCLUA_REG_DET_CTX, (void *)det_ctx); |
318 | 0 | } |
319 | | |
320 | | /** \brief get the per-rule DetectLuaData */ |
321 | | DetectLuaData *LuaStateGetDetectLuaData(lua_State *luastate) |
322 | 0 | { |
323 | 0 | return (DetectLuaData *)LuaStateGetReg(luastate, luaext_key_ld, SCLUA_REG_DETECT_LUA_DATA); |
324 | 0 | } |
325 | | |
326 | | void LuaStateSetDetectLuaData(lua_State *luastate, DetectLuaData *ld) |
327 | 34.0k | { |
328 | 34.0k | LuaStateSetReg(luastate, luaext_key_ld, SCLUA_REG_DETECT_LUA_DATA, (void *)ld); |
329 | 34.0k | } |
330 | | |
331 | | /** \brief get packet pointer from the lua state */ |
332 | | int LuaStateGetDirection(lua_State *luastate) |
333 | 0 | { |
334 | 0 | lua_pushlightuserdata(luastate, (void *)&lua_ext_key_direction); |
335 | 0 | lua_gettable(luastate, LUA_REGISTRYINDEX); |
336 | 0 | int dir = lua_toboolean(luastate, -1); |
337 | 0 | lua_pop(luastate, 1); |
338 | 0 | return dir; |
339 | 0 | } |
340 | | |
341 | | void LuaStateSetDirection(lua_State *luastate, int direction) |
342 | 0 | { |
343 | 0 | lua_pushlightuserdata(luastate, (void *)&lua_ext_key_direction); |
344 | 0 | lua_pushboolean(luastate, direction); |
345 | 0 | lua_settable(luastate, LUA_REGISTRYINDEX); |
346 | 0 | } |
347 | | |
348 | | /** \brief dump stack from lua state to screen */ |
349 | 0 | void LuaPrintStack(lua_State *state) { |
350 | 0 | int size = lua_gettop(state); |
351 | 0 | int i; |
352 | |
|
353 | 0 | for (i = 1; i <= size; i++) { |
354 | 0 | int type = lua_type(state, i); |
355 | 0 | printf("Stack size=%d, level=%d, type=%d, ", size, i, type); |
356 | |
|
357 | 0 | switch (type) { |
358 | 0 | case LUA_TFUNCTION: |
359 | 0 | printf("function %s", lua_tostring(state, i) ? "true" : "false"); |
360 | 0 | break; |
361 | 0 | case LUA_TBOOLEAN: |
362 | 0 | printf("bool %s", lua_toboolean(state, i) ? "true" : "false"); |
363 | 0 | break; |
364 | 0 | case LUA_TNUMBER: |
365 | 0 | printf("number %g", lua_tonumber(state, i)); |
366 | 0 | break; |
367 | 0 | case LUA_TSTRING: |
368 | 0 | printf("string `%s'", lua_tostring(state, i)); |
369 | 0 | break; |
370 | 0 | case LUA_TTABLE: |
371 | 0 | printf("table `%s'", lua_tostring(state, i)); |
372 | 0 | break; |
373 | 0 | default: |
374 | 0 | printf("other %s", lua_typename(state, type)); |
375 | 0 | break; |
376 | |
|
377 | 0 | } |
378 | 0 | printf("\n"); |
379 | 0 | } |
380 | 0 | } |
381 | | |
382 | | int LuaPushStringBuffer(lua_State *luastate, const uint8_t *input, size_t input_len) |
383 | 0 | { |
384 | | /* a NULL buffer is outside the lua_pushlstring contract even with |
385 | | * zero length; push the empty string instead (and clamp the length, |
386 | | * a NULL input has no content of its own) */ |
387 | 0 | if (input == NULL) { |
388 | 0 | input = (const uint8_t *)""; |
389 | 0 | input_len = 0; |
390 | 0 | } |
391 | 0 | lua_pushlstring(luastate, (char *)input, input_len); |
392 | 0 | return 1; |
393 | 0 | } |
394 | | |
395 | | int LuaPushInteger(lua_State *luastate, lua_Integer n) |
396 | 0 | { |
397 | 0 | lua_pushinteger(luastate, n); |
398 | 0 | return 1; |
399 | 0 | } |