forked from OISF/suricata
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
Add lua sandbox files
Showing
6 changed files
with
411 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
free(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 = realloc(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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* 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 <pyrojoe314@gmail.com> | ||
*/ | ||
|
||
#ifndef __UTIL_LUA_SANDBOX_H__ | ||
#define __UTIL_LUA_SANDBOX_H__ | ||
|
||
#ifndef HAVE_LUA | ||
/* If we don't have Lua, create a typedef for sb_lua_State so the | ||
* exported Lua functions don't fail the build. */ | ||
typedef void sb_lua_state; | ||
#else | ||
#include <stdlib.h> | ||
#include "lua.h" | ||
|
||
#if !defined(SANDBOX_ALLOC_CTX) | ||
#define SANDBOX_CTX "SANDBOX_CTX" | ||
#endif | ||
|
||
/* | ||
* Lua sandbox usage: The only needed changes to use the sandboxed lua state are | ||
* to replace calls to lua_newstate and lua_close with sb_newstate and sb_close | ||
* Additionally, sb_loadrestricted can be used to load a restricted set of packages | ||
* that prevent side effecting outside of the lua runtime | ||
*/ | ||
|
||
/* | ||
* Struct to store a lua_state and the additional metadata required to sandbox it | ||
*/ | ||
typedef struct sb_lua_state { | ||
lua_State *L; | ||
|
||
// Allocation limits | ||
uint64_t alloc_bytes; | ||
uint64_t alloc_limit; | ||
|
||
// Execution Limits | ||
uint64_t instruction_count; | ||
uint64_t instruction_limit; | ||
uint64_t hook_instruction_count; | ||
} sb_lua_state; | ||
|
||
enum sb_level { NONE, EXTERNAL_RESTRICTED, PERFORMANCE_RESTRICTED }; | ||
|
||
/* | ||
* Replaces luaL_newstate. Sets an upper bound for allocations and bytecode | ||
* instructions for the lua runtime on this state. | ||
* | ||
* alloclimit - maximium number of bytes lua can allocate before receiving out of memory. | ||
* A value of zero will not limit allocations | ||
* instructionlimit - maximum number of lua bytecode instructions before an error is thrown | ||
* A value of zero will not limit the number of instructions | ||
*/ | ||
lua_State *sb_newstate(uint64_t alloclimit, uint64_t instructionlimit); | ||
|
||
/* | ||
* Replaces lua_close. Handles freeing the sb_lua_state | ||
*/ | ||
void sb_close(lua_State *sb); | ||
|
||
/* | ||
* Resets the instruction counter for the sandbox to 0 | ||
*/ | ||
void sb_resetinstructioncounter(lua_State *sb); | ||
|
||
/* | ||
* Sets the maximum number of lua instructions before erroring out | ||
*/ | ||
void sb_setinstructionlimit(lua_State *L, uint64_t instruction_limit); | ||
|
||
/* | ||
* Replaces luaL_openlibs. Only opens allowed paackages for the sandbox and | ||
* masks out dangerous functions from the base. | ||
*/ | ||
LUALIB_API void sb_loadrestricted(lua_State *L); | ||
|
||
#endif /* HAVE_LUA */ | ||
|
||
#endif /* __UTIL_LUA_SANDBOX_H__ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters