Lua sandbox

Change table format to handle functions never to be included.
Clean up bit masks and tables of functions.
Remove some old comments and out-of-date code.
This commit is contained in:
nhkeni
2022-05-07 17:45:36 -04:00
parent b74ba33eed
commit c6c61a1419
2 changed files with 150 additions and 92 deletions

View File

@@ -514,12 +514,7 @@ typedef struct nhl_sandbox_info {
#define NHL_SB_SAFE 0x80000000
/* Access to Lua version information. */
#define NHL_SB_VERSION 0x40000000
#ifdef notyet
/* XXX These need to be replaced. */
#define NHL_SB_CANREAD 0x20000000
#define NHL_SB_CANWRITE 0x10000000
#endif
/* Debugging library - highly unsafe. */
/* Debugging library - mostly unsafe. */
#define NHL_SB_DEBUGGING 0x08000000
/* Use with memlimit/steps/perpcall to get usage. */
#define NHL_SB_REPORT 0x04000000
@@ -528,24 +523,30 @@ typedef struct nhl_sandbox_info {
/* Low level groups. If you need these, you probably need to define
* a new high level group instead. */
#define NHL_SB_DB 0x00000001
#define NHL_SB_STRING 0x00000002
#define NHL_SB_TABLE 0x00000004
#define NHL_SB_COROUTINE 0x00000008
#define NHL_SB_MATH 0x00000010
#define NHL_SB_UTF8 0x00000020
#define NHL_SB_STRING 0x00000001
#define NHL_SB_TABLE 0x00000002
#define NHL_SB_COROUTINE 0x00000004
#define NHL_SB_MATH 0x00000008
#define NHL_SB_UTF8 0x00000010
#ifdef notyet
#define NHL_SB_PACKAGE 0x00000040
#define NHL_SB_IO 0x00000080
#define NHL_SB_OS 0x00000100
#define NHL_SB_IO 0x00000020
#endif
#define NHL_SB_OS 0x00000040
#define NHL_SB_BASEMASK 0x0001f000
#define NHL_SB_BASE_BASE 0x00001000
#define NHL_SB_BASE_ERROR 0x00002000
#define NHL_SB_BASE_META 0x00004000
#define NHL_SB_BASE_GC 0x00008000
#define NHL_SB_BASE_UNSAFE 0x00010000
#define NHL_SB_BASEMASK 0x00000f80
#define NHL_SB_BASE_BASE 0x00000080
#define NHL_SB_BASE_ERROR 0x00000100
#define NHL_SB_BASE_META 0x00000200
#define NHL_SB_BASE_GC 0x00000400
#define NHL_SB_BASE_UNSAFE 0x00000800
#define NHL_SB_DBMASK 0x00003000
#define NHL_SB_DB_DB 0x00001000
#define NHL_SB_DB_SAFE 0x00002000
#define NHL_SB_OSMASK 0x0000c000
#define NHL_SB_OS_TIME 0x00004000
#define NHL_SB_OS_FILES 0x00008000
#define NHL_SB_ALL 0x0000ffff

View File

@@ -1754,42 +1754,127 @@ RESTORE_WARNINGS
***/
#ifdef NHL_SANDBOX
enum ewhen {NEVER, IFFLAG, EOT};
struct e {
enum ewhen when;
const char *fnname;
};
/* NHL_BASE_BASE - safe things */
static const char *ct_base_base[] = {
"ipairs", "next", "pairs", "pcall", "rawequal", "rawlen", "select",
"tonumber", "tostring", "type", "xpcall", NULL
static struct e ct_base_base[] = {
{IFFLAG, "ipairs"},
{IFFLAG, "next"},
{IFFLAG, "pairs"},
{IFFLAG, "pcall"},
{IFFLAG, "select"},
{IFFLAG, "tonumber"},
{IFFLAG, "tostring"},
{IFFLAG, "type"},
{IFFLAG, "xpcall"},
{EOT, NULL}
};
/* NHL_BASE_ERROR - not really safe - might not want Lua to kill the process */
static const char *ct_base_error[] = {
"assert", /* ok, calls error */
"error", /* ok, calls G->panic */
/* "print", not ok - calls lua_writestring/lua_writeline -> stdout*/
/* "warn", not ok - calls lua_writestringerror -> stderr */
NULL
static struct e ct_base_error[] = {
{IFFLAG, "assert"}, /* ok, calls error */
{IFFLAG, "error"}, /* ok, calls G->panic */
{NEVER, "print"}, /*not ok - calls lua_writestring/lua_writeline -> stdout*/
{NEVER, "warn"}, /*not ok - calls lua_writestringerror -> stderr*/
{EOT, NULL}
};
/* NHL_BASE_META - metatable access */
static const char *ct_base_meta[] = {
"getmetatable", "rawget", "rawset", "setmetatable", NULL
static struct e ct_base_meta[] = {
{IFFLAG, "getmetatable"},
{IFFLAG, "rawequal"},
{IFFLAG, "rawget"},
{IFFLAG, "rawlen"},
{IFFLAG, "rawset"},
{IFFLAG, "setmetatable"},
{EOT, NULL}
};
/* NHL_BASE_GC - questionable safety */
static const char *ct_base_iffy[] = {
"collectgarbage", NULL
static struct e ct_base_iffy[] = {
{IFFLAG, "collectgarbage"},
{EOT, NULL}
};
/* NHL_BASE_UNSAFE - include only if required */
static const char *ct_base_unsafe[] = {
"dofile", "loadfile", "load", NULL
static struct e ct_base_unsafe[] = {
{IFFLAG, "dofile"},
{IFFLAG, "loadfile"},
{IFFLAG, "load"},
{EOT, NULL}
};
/* no ct_co_ tables; all fns at same level of concern */
/* no ct_string_ tables; all fns at same level of concern */
/* no ct_table_ tables; all fns at same level of concern (but
sort can take a lot of time and can't be caught by the step limit */
/* no ct_utf8_ tables; all fns at same level of concern */
/* possible ct_debug tables - likely to need changes */
static struct e ct_debug_debug[] = {
{NEVER, "debug"}, /* uses normal I/O so needs re-write */
{IFFLAG, "getuservalue"},
{NEVER, "gethook"}, /* see sethook */
{IFFLAG, "getinfo"},
{IFFLAG, "getlocal"},
{IFFLAG, "getregistry"},
{IFFLAG, "getmetatable"},
{IFFLAG, "getupvalue"},
{IFFLAG, "upvaluejoin"},
{IFFLAG, "upvalueid"},
{IFFLAG, "setuservalue"},
{NEVER, "sethook"}, /* used for memory and step limits */
{IFFLAG, "setlocal"},
{IFFLAG, "setmetatable"},
{IFFLAG, "setupvalue"},
{IFFLAG, "setcstacklimit"},
{EOT, NULL}
};
static struct e ct_debug_safe[] = {
{IFFLAG, "traceback"},
{EOT, NULL}
};
/* possible ct_os_ tables */
static struct e ct_os_time[] = {
{IFFLAG, "clock"}, /* is this portable? XXX */
{IFFLAG, "date"},
{IFFLAG, "difftime"},
{IFFLAG, "time"},
{EOT, NULL}
};
static struct e ct_os_files[] = {
{NEVER, "execute"}, /* not portable */
{NEVER, "exit"},
{NEVER, "getenv"},
{IFFLAG, "remove"},
{IFFLAG, "rename"},
{NEVER, "setlocale"},
{NEVER, "tmpname"},
{EOT, NULL}
};
#define DROPIF(flag, lib, ct) \
nhl_clearfromtable(L, !!(lflags & flag), lib, ct)
static void
nhl_clearfromtable(lua_State *L, int tndx, const char **todo)
nhl_clearfromtable(lua_State *L, int flag, int tndx, struct e *todo)
{
while(*todo){
while(todo->when != EOT){
lua_pushnil(L);
lua_setfield(L, tndx, *todo++);
/* if we load the library at all, NEVER items must be erased
* and IFFLAG items should be erased if !flag */
if(todo->when==NEVER || !flag) {
lua_setfield(L, tndx, todo->fnname);
}
todo++;
}
}
#endif
@@ -2014,27 +2099,14 @@ DISABLE_WARNING_CONDEXPR_IS_CONSTANT
#ifdef NHL_SANDBOX
static void
nhlL_openlibs(lua_State *L, uint32_t lflags){
uint32_t needbase;
/* translate lflags from user-friendly to internal */
if (NHL_SB_DEBUGGING & lflags){
#if 1 /* XXX */
lflags |= NHL_SB_DB;
/* XXX
Should these be available as safe or as a low level group?
debug.getinfo
debug.traceback?
*/
#endif
lflags |= NHL_SB_DB_SAFE;
}
/* only for debugging the sandbox integration */
if (NHL_SB_ALL & lflags){
lflags = -1;
} else if ((NHL_SB_SAFE
#ifdef notyet
|NHL_SB_CANREAD
#endif
) & lflags){
} else if (NHL_SB_SAFE & lflags){
lflags |= NHL_SB_BASE_BASE;
lflags |= NHL_SB_COROUTINE;
lflags |= NHL_SB_TABLE;
@@ -2043,30 +2115,22 @@ debug.traceback?
lflags |= NHL_SB_UTF8;
} else if (NHL_SB_VERSION){
lflags |= NHL_SB_BASE_BASE;
}
#ifdef notyet
} else if (NHL_SB_CANREAD & lflags){
/* QQQ */
/*
canread may be wrong.
How about:
- sets of fns (as below, as base)
/* Handling I/O is complex, so it's not available yet. I'll
finish it if and when we need it. (keni)
- hooked open; array of tuples of (r/w/rw/a/etc, directory pat, file pat)
XXX
really don't have anything here
because IO is too broad?
we need to split it like BASE - load then delete:
SAFEIO:
{"close", io_close}, but with no args closes default output, so needs hook
{"flush", io_flush},
{"lines", io_lines}, hook due to filename
{"open", io_open}, but we need a hooked version:
{"open", io_open}, hooked version:
only safe if mode not present or == "r"
or WRITEIO
only safe if path has no slashes
XXX probably need to be: matches port-specific list of paths
WRITEIO needs a different list
dlb integration?????
dlb integration?
may need to #define l_getc (but that wouldn't hook core)
may need to #define fopen/fread/fwrite/feof/ftell (etc?)
ugh: lauxlib.c uses getc() below luaL_loadfilex
@@ -2084,39 +2148,24 @@ UNSAFEIO:
{"tmpfile", io_tmpfile},
*/
#endif
}
/*
multiple levels - io.*, FILE.* - can we hook FILE.*?
see liolib.c:{meta, createmeta, luaopen_io}
// do we need anything else? meta?
*/
needbase = lflags & NHL_SB_BASEMASK;
if(needbase){
if(lflags & NHL_SB_BASEMASK){
int baselib;
/* load the entire library ... */
luaL_requiref(L, LUA_GNAME, luaopen_base, 1);
int baselib = lua_gettop(L);
/* now remove everything not requested */
uint16_t rejectflags = ~lflags;
#define DROPIF(flag, x, table) \
if(rejectflags & flag){ nhl_clearfromtable(L, x, table); }
baselib = lua_gettop(L);
/* ... and remove anything unsupported or not requested */
DROPIF(NHL_SB_BASE_BASE, baselib, ct_base_base);
DROPIF(NHL_SB_BASE_ERROR, baselib, ct_base_error);
DROPIF(NHL_SB_BASE_META, baselib, ct_base_meta);
DROPIF(NHL_SB_BASE_GC, baselib, ct_base_iffy);
DROPIF(NHL_SB_BASE_UNSAFE, baselib, ct_base_unsafe);
#undef DROPIF
lua_pop(L, 1);
}
#ifdef notyet
if(lflags & NHL_SB_PACKAGE){
luaL_requiref(L, LUA_LOADLIBNAME, luaopen_package, 1);
lua_pop(L, 1);
}
#endif
if(lflags & NHL_SB_COROUTINE){
luaL_requiref(L, LUA_COLIBNAME, luaopen_coroutine, 1);
lua_pop(L, 1);
@@ -2132,12 +2181,16 @@ multiple levels - io.*, FILE.* - can we hook FILE.*?
if(!hook_open(L))
panic("can't hook io.open");
}
// maybe ok: time, difftime, getenv clock date
if(lflags & NHL_SB_OS){
#endif
if(lflags & NHL_SB_OSMASK){
int oslib;
luaL_requiref(L, LUA_OSLIBNAME, luaopen_os, 1);
oslib = lua_gettop(L);
DROPIF(NHL_SB_OS_TIME, oslib, ct_os_time);
DROPIF(NHL_SB_OS_FILES, oslib, ct_os_files);
lua_pop(L, 1);
}
#endif
if(lflags & NHL_SB_STRING){
luaL_requiref(L, LUA_STRLIBNAME, luaopen_string, 1);
lua_pop(L, 1);
@@ -2153,8 +2206,12 @@ multiple levels - io.*, FILE.* - can we hook FILE.*?
luaL_requiref(L, LUA_UTF8LIBNAME, luaopen_utf8, 1);
lua_pop(L, 1);
}
if(lflags & NHL_SB_DB){
if(lflags & NHL_SB_DBMASK){
int dblib;
luaL_requiref(L, LUA_DBLIBNAME, luaopen_debug, 1);
dblib = lua_gettop(L);
DROPIF(NHL_SB_DB_DB, dblib, ct_debug_debug);
DROPIF(NHL_SB_DB_SAFE, dblib, ct_debug_safe);
lua_pop(L, 1);
}
}