#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
+#include "util-debug.h"
#include "util-debug.h"
#include "util-validate.h"
#define SANDBOX_CTX "SANDBOX_CTX"
-typedef struct BlockedFunction {
- const char *module;
- const char *name;
-} BlockedFunction;
-
static void HookFunc(lua_State *L, lua_Debug *ar);
static int OpenSandbox(lua_State *L);
}
}
+/**
+ * Function put in place of Lua functions that are blocked.
+ *
+ * TODO: Might want to create a version of this for each library that
+ * has blocked functions, so it can display the name of the
+ * library. As it doesn't appear that can be retrieved.
+ */
+static int LuaBlockedFunction(lua_State *L)
+{
+ lua_Debug ar;
+ lua_getstack(L, 0, &ar);
+ lua_getinfo(L, "n", &ar);
+ if (ar.name) {
+ luaL_error(L, "Blocked Lua function called: %s", ar.name);
+ } else {
+ luaL_error(L, "Blocked Lua function: name not available");
+ }
+ return -1;
+}
+
+/**
+ * Check if a Lua function in a specific module is allowed.
+ *
+ * This is essentially an allow list for Lua functions.
+ */
+static bool IsAllowed(const char *module, const char *fname)
+{
+ static const char *base_allowed[] = {
+ "assert",
+ "ipairs",
+ "next",
+ "pairs",
+ "print",
+ "rawequal",
+ "rawlen",
+ "select",
+ "tonumber",
+ "tostring",
+ "type",
+ "warn",
+ "rawget",
+ "rawset",
+ "error",
+ NULL,
+ };
+
+ /* Allow all. */
+ static const char *table_allowed[] = {
+ "concat",
+ "insert",
+ "move",
+ "pack",
+ "remove",
+ "sort",
+ "unpack",
+ NULL,
+ };
+
+ /* Allow all. */
+ static const char *string_allowed[] = {
+ "byte",
+ "char",
+ "dump",
+ "find",
+ "format",
+ "gmatch",
+ "gsub",
+ "len",
+ "lower",
+ "match",
+ "pack",
+ "packsize",
+ "rep",
+ "reverse",
+ "sub",
+ "unpack",
+ "upper",
+ NULL,
+ };
+
+ /* Allow all. */
+ static const char *math_allowed[] = {
+ "abs",
+ "acos",
+ "asin",
+ "atan",
+ "atan2",
+ "ceil",
+ "cos",
+ "cosh",
+ "deg",
+ "exp",
+ "floor",
+ "fmod",
+ "frexp",
+ "ldexp",
+ "log",
+ "log10",
+ "max",
+ "min",
+ "modf",
+ "pow",
+ "rad",
+ "random",
+ "randomseed",
+ "sin",
+ "sinh",
+ "sqrt",
+ "tan",
+ "tanh",
+ "tointeger",
+ "type",
+ "ult",
+ NULL,
+ };
+
+ /* Allow all. */
+ static const char *utf8_allowed[] = {
+ "offset",
+ "len",
+ "codes",
+ "char",
+ "codepoint",
+ NULL,
+ };
+
+ const char **allowed = NULL;
+
+ if (strcmp(module, LUA_GNAME) == 0) {
+ allowed = base_allowed;
+ } else if (strcmp(module, LUA_TABLIBNAME) == 0) {
+ allowed = table_allowed;
+ } else if (strcmp(module, LUA_STRLIBNAME) == 0) {
+ allowed = string_allowed;
+ } else if (strcmp(module, LUA_MATHLIBNAME) == 0) {
+ allowed = math_allowed;
+ } else if (strcmp(module, LUA_UTF8LIBNAME) == 0) {
+ allowed = utf8_allowed;
+ } else {
+ /* This is a programming error. */
+ FatalError("Unknown Lua module %s", module);
+ }
+
+ if (allowed) {
+ for (int i = 0; allowed[i] != NULL; i++) {
+ if (strcmp(allowed[i], fname) == 0) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
/**
* Set of libs that are allowed and loaded into the Lua state.
*/
{ LUA_STRLIBNAME, luaopen_string },
{ LUA_MATHLIBNAME, luaopen_math },
{ LUA_UTF8LIBNAME, luaopen_utf8 },
-
- /* TODO: Review these libs... */
-#if 0
- {LUA_LOADLIBNAME, luaopen_package},
- {LUA_COLIBNAME, luaopen_coroutine},
- {LUA_IOLIBNAME, luaopen_io},
- {LUA_OSLIBNAME, luaopen_os},
-#endif
-
- /* What is this for? */
- { LUA_DBLIBNAME, OpenSandbox }, // TODO: remove this from restricted
-
{ NULL, NULL }
// clang-format on
};
-// TODO: should we block raw* functions?
-// TODO: Will we ever need to block a subset of functions more than one level deep?
-static const BlockedFunction BlockedFunctions[] = {
- // clang-format off
- { LUA_GNAME, "collectgarbage" },
- { LUA_GNAME, "dofile" },
- { LUA_GNAME, "getmetatable" },
- { LUA_GNAME, "loadfile" },
- { LUA_GNAME, "load" },
- { LUA_GNAME, "pcall" },
- { LUA_GNAME, "setmetatable" },
- { LUA_GNAME, "xpcall" },
-
- /* TODO: probably don't need to block this for normal restricted
- * since we have memory limit */
- { LUA_STRLIBNAME, "rep" },
- { NULL, NULL }
- // clang-format on
-};
-
-static void LoadAllowedLibs(lua_State *L, const luaL_Reg *libs)
+/**
+ * Load allowed Lua libraries into the state.
+ *
+ * Functions from each library that are not in the allowed list are
+ * replaced with LuaBlockedFunction.
+ */
+void SCLuaSbLoadLibs(lua_State *L)
{
const luaL_Reg *lib;
- /* "require" functions from 'loadedlibs' and set results to global table */
- for (lib = libs; lib->func; lib++) {
+
+ for (lib = AllowedLibs; lib->func; lib++) {
luaL_requiref(L, lib->name, lib->func, 1);
- lua_pop(L, 1); /* remove lib */
+ lua_pop(L, 1);
+ /* Iterate over all the functions in the just loaded table and
+ * replace functions now on the allow list with our blocked
+ * function placeholder. */
+ lua_getglobal(L, lib->name);
+ lua_pushnil(L);
+ while (lua_next(L, -2)) {
+ if (lua_type(L, -1) == LUA_TFUNCTION) {
+ const char *name = lua_tostring(L, -2);
+ if (!IsAllowed(lib->name, name)) {
+ SCLogDebug("Blocking Lua function %s.%s", lib->name, name);
+ lua_pushstring(L, name);
+ lua_pushcfunction(L, LuaBlockedFunction);
+ lua_settable(L, -5);
+ } else {
+ SCLogDebug("Allowing Lua function %s.%s", lib->name, name);
+ }
+ }
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 1);
}
}
/**
- * Apply function blocking by replacing blocked functions with a nil.
+ * \brief Allocate a new Lua sandbox.
+ *
+ * \returns An allocated sandbox state or NULL if memory allocation
+ * fails.
*/
-static void ApplyBlockedFunctions(lua_State *L, const BlockedFunction *funcs)
-{
- const BlockedFunction *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
-}
-
-void SCLuaSbLoadRestricted(lua_State *L)
-{
- LoadAllowedLibs(L, AllowedLibs);
- ApplyBlockedFunctions(L, BlockedFunctions);
-}
-
lua_State *SCLuaSbStateNew(uint64_t alloclimit, uint64_t instructionlimit)
{
SCLuaSbState *sb = SCCalloc(1, sizeof(SCLuaSbState));
if (sb == NULL) {
- // Out of memory. Error code?
return NULL;
}