Skip to content

Commit

Permalink
lua: Add lua sandbox for detection rules
Browse files Browse the repository at this point in the history
  • Loading branch information
J0eJ0h committed Jan 26, 2024
1 parent ff0438e commit 1b3e112
Show file tree
Hide file tree
Showing 6 changed files with 411 additions and 7 deletions.
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,7 @@ noinst_HEADERS = \
util-lua-hassh.h \
util-lua-http.h \
util-lua-ja3.h \
util-lua-sandbox.h \
util-lua-smtp.h \
util-lua-ssh.h \
util-lua-tls.h \
Expand Down Expand Up @@ -1175,6 +1176,7 @@ libsuricata_c_a_SOURCES = \
util-lua-hassh.c \
util-lua-http.c \
util-lua-ja3.c \
util-lua-sandbox.c \
util-lua-smtp.c \
util-lua-ssh.c \
util-lua-tls.c \
Expand Down
18 changes: 11 additions & 7 deletions src/detect-lua.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ void DetectLuaRegister(void)
#else /* HAVE_LUA */

#include "util-lua.h"
#include "util-lua-sandbox.h"

static int DetectLuaMatch (DetectEngineThreadCtx *,
Packet *, const Signature *, const SigMatchCtx *);
Expand All @@ -104,6 +105,9 @@ static void DetectLuaRegisterTests(void);
static void DetectLuaFree(DetectEngineCtx *, void *);
static int g_smtp_generic_list_id = 0;

// TODO: move to config
static const uint64_t g_lua_alloc_limit = 500000, g_lua_instruction_limit = 500000;

/**
* \brief Registration function for keyword: lua
*/
Expand Down Expand Up @@ -603,7 +607,7 @@ static void *DetectLuaThreadInit(void *data)
t->alproto = lua->alproto;
t->flags = lua->flags;

t->luastate = LuaGetState();
t->luastate = sb_newstate(g_lua_alloc_limit, g_lua_instruction_limit);
if (t->luastate == NULL) {
SCLogError("luastate pool depleted");
goto error;
Expand Down Expand Up @@ -649,7 +653,7 @@ static void *DetectLuaThreadInit(void *data)

error:
if (t->luastate != NULL)
LuaReturnState(t->luastate);
sb_close(t->luastate);
SCFree(t);
return NULL;
}
Expand All @@ -659,7 +663,7 @@ static void DetectLuaThreadFree(void *ctx)
if (ctx != NULL) {
DetectLuaThreadData *t = (DetectLuaThreadData *)ctx;
if (t->luastate != NULL)
LuaReturnState(t->luastate);
sb_close(t->luastate);
SCFree(t);
}
}
Expand Down Expand Up @@ -705,10 +709,10 @@ static int DetectLuaSetupPrime(DetectEngineCtx *de_ctx, DetectLuaData *ld, const
{
int status;

lua_State *luastate = luaL_newstate();
lua_State *luastate = sb_newstate(g_lua_alloc_limit, g_lua_instruction_limit);
if (luastate == NULL)
return -1;
luaL_openlibs(luastate);
luaL_openlibs(luastate); // TODO: get sandbox config and load appropriate libs

/* hackish, needed to allow unittests to pass buffers as scripts instead of files */
#ifdef UNITTESTS
Expand Down Expand Up @@ -987,10 +991,10 @@ static int DetectLuaSetupPrime(DetectEngineCtx *de_ctx, DetectLuaData *ld, const

/* pop the table */
lua_pop(luastate, 1);
lua_close(luastate);
sb_close(luastate);
return 0;
error:
lua_close(luastate);
sb_close(luastate);
return -1;
}

Expand Down
1 change: 1 addition & 0 deletions src/detect-lua.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#ifdef HAVE_LUA

#include "util-lua.h"
#include "util-lua-sandbox.h"

typedef struct DetectLuaThreadData {
lua_State *luastate;
Expand Down
298 changes: 298 additions & 0 deletions src/util-lua-sandbox.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
/* Copyright (C) 2014-2023 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/

/**
* \file
*
* \author Jo Johnson <[email protected]>
*/

#include "suricata-common.h"

#ifdef HAVE_LUA

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.h"

#include "lauxlib.h"
#include "lualib.h"

#include "util-lua-sandbox.h"

typedef struct sb_block_function {
const char *module;
const char *name;
} sb_block_function;

static void sb_hook(lua_State *L, lua_Debug *ar);
LUAMOD_API int luaopen_sandbox(lua_State *L);

static void *sb_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
{
(void)ud;
(void)osize; /* not used */
sb_lua_state *ctx = (sb_lua_state *)ud;
if (nsize == 0) {
if (ptr != NULL) {
// ASSERT: alloc_bytes > osize
ctx->alloc_bytes -= osize;
}
SCFree(ptr);
return NULL;
} else {
// We can be a bit sloppy on the alloc limit since it's not supposed to be hit.
// ASSERT: ctx->alloc_bytes + nsize > ctx->alloc_bytes
if (ctx->alloc_bytes + nsize > ctx->alloc_limit) {
// TODO: Trace in a better way
return NULL;
}
void *nptr = SCRealloc(ptr, nsize);

ctx->alloc_bytes += nsize;
return nptr;
}
}

/*
** These are the set of libs allowed in the restricted lua sandbox
*/
static const luaL_Reg sb_restrictedlibs[] = { { LUA_GNAME, luaopen_base },
// {LUA_LOADLIBNAME, luaopen_package},
// {LUA_COLIBNAME, luaopen_coroutine},
{ LUA_TABLIBNAME, luaopen_table },
//{LUA_IOLIBNAME, luaopen_io},
// {LUA_OSLIBNAME, luaopen_os},
{ LUA_STRLIBNAME, luaopen_string }, { LUA_MATHLIBNAME, luaopen_math },
{ LUA_UTF8LIBNAME, luaopen_utf8 },
{ LUA_DBLIBNAME, luaopen_sandbox }, // TODO: remove this from restricted
{ NULL, NULL } };

// TODO: should we block raw* functions?
// TODO: Will we ever need to block a subset of functions more than one level deep?
static const sb_block_function sb_restrictedfuncs[] = { { LUA_GNAME, "collectgarbage" },
{ LUA_GNAME, "dofile" }, { LUA_GNAME, "getmetatable" }, { LUA_GNAME, "loadfile" },
{ LUA_GNAME, "load" }, { LUA_GNAME, "pcall" }, { LUA_GNAME, "setmetatable" },
{ LUA_GNAME, "xpcall" },

{ LUA_STRLIBNAME, "rep" }, // TODO: probably don't need to block this for normal restricted
// since we have memory limit
{ NULL, NULL } };

static void sb_loadlibs(lua_State *L, const luaL_Reg *libs)
{
const luaL_Reg *lib;
/* "require" functions from 'loadedlibs' and set results to global table */
for (lib = libs; lib->func; lib++) {
luaL_requiref(L, lib->name, lib->func, 1);
lua_pop(L, 1); /* remove lib */
}
}

static void sb_block_functions(lua_State *L, const sb_block_function *funcs)
{
const sb_block_function *func;

// set target functions to nil
lua_pushglobaltable(L);
for (func = funcs; func->module; func++) {
lua_pushstring(L, func->module);
lua_gettable(L, -2); // load module to stack
lua_pushstring(L, func->name);
lua_pushnil(L);
lua_settable(L, -3);
lua_pop(L, 1); // remove module from the stack
}
lua_pop(L, 1); // remove global table
}

LUALIB_API void sb_loadrestricted(lua_State *L)
{
sb_loadlibs(L, sb_restrictedlibs);
sb_block_functions(L, sb_restrictedfuncs);
}

lua_State *sb_newstate(uint64_t alloclimit, uint64_t instructionlimit)
{
sb_lua_state *sb = SCMalloc(sizeof(sb_lua_state));
sb->alloc_limit = alloclimit;
sb->alloc_bytes = 0;
sb->hook_instruction_count = 100;
sb->instruction_limit = instructionlimit;

sb->L = lua_newstate(sb_alloc, sb); /* create state */
if (sb == NULL) {
// Out of memory. Error code?
return NULL;
}
if (sb->L == NULL) {
// TODO: log or error code?
free(sb);
return NULL;
}

lua_pushstring(sb->L, SANDBOX_CTX);
lua_pushlightuserdata(sb->L, sb);
lua_settable(sb->L, LUA_REGISTRYINDEX);

lua_sethook(sb->L, sb_hook, LUA_MASKCOUNT, sb->hook_instruction_count);
return sb->L;
}

static sb_lua_state *sb_get_ctx(lua_State *L)
{
lua_pushstring(L, SANDBOX_CTX);
lua_gettable(L, LUA_REGISTRYINDEX);
sb_lua_state *ctx = lua_touserdata(L, -1);
// TODO: log if null?
lua_pop(L, 1);
return ctx;
}

void sb_close(lua_State *L)
{
sb_lua_state *sb = sb_get_ctx(L);
lua_close(sb->L);
SCFree(sb);
}

static void sb_hook(lua_State *L, lua_Debug *ar)
{
(void)ar;
sb_lua_state *sb = sb_get_ctx(L);

sb->instruction_count += sb->hook_instruction_count;

if (sb->instruction_limit > 0 && sb->instruction_count > sb->instruction_limit) {
// TODO: do we care enough for a full traceback here?
luaL_error(L, "Instruction limit exceeded");
}
}

void sb_resetinstructioncounter(lua_State *L)
{
sb_lua_state *sb = sb_get_ctx(L);
if (sb != NULL) {
sb->instruction_count = 0;
lua_sethook(L, sb_hook, LUA_MASKCOUNT, sb->hook_instruction_count);
}
}

void sb_setinstructionlimit(lua_State *L, uint64_t instruction_limit)
{
sb_lua_state *ctx = sb_get_ctx(L);
if (ctx != NULL) {
ctx->instruction_limit = instruction_limit;
}
}

static uint64_t sb_getinstructioncount(lua_State *L)
{
sb_lua_state *ctx = sb_get_ctx(L);
if (ctx != NULL) {
return ctx->instruction_count;
}
return 0;
}

static int sb_Ltotalalloc(lua_State *L)
{
sb_lua_state *ctx = sb_get_ctx(L);
if (ctx != NULL) {
lua_pushinteger(L, ctx->alloc_bytes);
} else {
lua_pushinteger(L, 0);
}
return 1;
}

static int sb_Lgetalloclimit(lua_State *L)
{
sb_lua_state *ctx = sb_get_ctx(L);
if (ctx != NULL) {
lua_pushinteger(L, ctx->alloc_limit);
} else {
lua_pushinteger(L, 0);
}
return 1;
}

static int sb_Lsetalloclimit(lua_State *L)
{
sb_lua_state *ctx = sb_get_ctx(L);
if (ctx != NULL) {
ctx->alloc_limit = luaL_checkinteger(L, 1);
}
return 0;
}

/*
static int sb_Lsetlevel(lua_State *L)
{
lua_Integer level = luaL_checkinteger(L, 1);
lua_pushnil(L);
lua_setglobal(L, "debug");
return 0;
}
*/

static int sb_Lgetinstructioncount(lua_State *L)
{
lua_pushinteger(L, sb_getinstructioncount(L));
return 1;
}

static int sb_Lgetinstructionlimit(lua_State *L)
{
sb_lua_state *ctx = sb_get_ctx(L);
if (ctx != NULL) {
lua_pushinteger(L, ctx->instruction_limit);
} else {
lua_pushinteger(L, 0);
}
return 1;
}

static int sb_Lsetinstructionlimit(lua_State *L)
{
sb_setinstructionlimit(L, luaL_checkinteger(L, 1));
return 0;
}

static int sb_Lresetinstructioncount(lua_State *L)
{
sb_resetinstructioncounter(L);
return 0;
}

static const luaL_Reg sblib[] = { { "totalalloc", sb_Ltotalalloc },
{ "getalloclimit", sb_Lgetalloclimit }, { "setalloclimit", sb_Lsetalloclimit },
// { "setlevel", sb_Lsetlevel },
{ "instructioncount", sb_Lgetinstructioncount },
{ "getinstructionlimit", sb_Lgetinstructionlimit },
{ "setinstructionlimit", sb_Lsetinstructionlimit },
{ "resetinstructioncount", sb_Lresetinstructioncount }, { NULL, NULL } };

LUAMOD_API int luaopen_sandbox(lua_State *L)
{
luaL_newlib(L, sblib);
return 1;
}

#endif
Loading

0 comments on commit 1b3e112

Please sign in to comment.