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:
+22
-21
@@ -514,12 +514,7 @@ typedef struct nhl_sandbox_info {
|
|||||||
#define NHL_SB_SAFE 0x80000000
|
#define NHL_SB_SAFE 0x80000000
|
||||||
/* Access to Lua version information. */
|
/* Access to Lua version information. */
|
||||||
#define NHL_SB_VERSION 0x40000000
|
#define NHL_SB_VERSION 0x40000000
|
||||||
#ifdef notyet
|
/* Debugging library - mostly unsafe. */
|
||||||
/* XXX These need to be replaced. */
|
|
||||||
#define NHL_SB_CANREAD 0x20000000
|
|
||||||
#define NHL_SB_CANWRITE 0x10000000
|
|
||||||
#endif
|
|
||||||
/* Debugging library - highly unsafe. */
|
|
||||||
#define NHL_SB_DEBUGGING 0x08000000
|
#define NHL_SB_DEBUGGING 0x08000000
|
||||||
/* Use with memlimit/steps/perpcall to get usage. */
|
/* Use with memlimit/steps/perpcall to get usage. */
|
||||||
#define NHL_SB_REPORT 0x04000000
|
#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
|
/* Low level groups. If you need these, you probably need to define
|
||||||
* a new high level group instead. */
|
* a new high level group instead. */
|
||||||
#define NHL_SB_DB 0x00000001
|
#define NHL_SB_STRING 0x00000001
|
||||||
#define NHL_SB_STRING 0x00000002
|
#define NHL_SB_TABLE 0x00000002
|
||||||
#define NHL_SB_TABLE 0x00000004
|
#define NHL_SB_COROUTINE 0x00000004
|
||||||
#define NHL_SB_COROUTINE 0x00000008
|
#define NHL_SB_MATH 0x00000008
|
||||||
#define NHL_SB_MATH 0x00000010
|
#define NHL_SB_UTF8 0x00000010
|
||||||
#define NHL_SB_UTF8 0x00000020
|
|
||||||
#ifdef notyet
|
#ifdef notyet
|
||||||
#define NHL_SB_PACKAGE 0x00000040
|
#define NHL_SB_IO 0x00000020
|
||||||
#define NHL_SB_IO 0x00000080
|
|
||||||
#define NHL_SB_OS 0x00000100
|
|
||||||
#endif
|
#endif
|
||||||
|
#define NHL_SB_OS 0x00000040
|
||||||
|
|
||||||
#define NHL_SB_BASEMASK 0x0001f000
|
#define NHL_SB_BASEMASK 0x00000f80
|
||||||
#define NHL_SB_BASE_BASE 0x00001000
|
#define NHL_SB_BASE_BASE 0x00000080
|
||||||
#define NHL_SB_BASE_ERROR 0x00002000
|
#define NHL_SB_BASE_ERROR 0x00000100
|
||||||
#define NHL_SB_BASE_META 0x00004000
|
#define NHL_SB_BASE_META 0x00000200
|
||||||
#define NHL_SB_BASE_GC 0x00008000
|
#define NHL_SB_BASE_GC 0x00000400
|
||||||
#define NHL_SB_BASE_UNSAFE 0x00010000
|
#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
|
#define NHL_SB_ALL 0x0000ffff
|
||||||
|
|
||||||
|
|||||||
+128
-71
@@ -1754,42 +1754,127 @@ RESTORE_WARNINGS
|
|||||||
***/
|
***/
|
||||||
#ifdef NHL_SANDBOX
|
#ifdef NHL_SANDBOX
|
||||||
|
|
||||||
|
enum ewhen {NEVER, IFFLAG, EOT};
|
||||||
|
struct e {
|
||||||
|
enum ewhen when;
|
||||||
|
const char *fnname;
|
||||||
|
};
|
||||||
|
|
||||||
/* NHL_BASE_BASE - safe things */
|
/* NHL_BASE_BASE - safe things */
|
||||||
static const char *ct_base_base[] = {
|
static struct e ct_base_base[] = {
|
||||||
"ipairs", "next", "pairs", "pcall", "rawequal", "rawlen", "select",
|
{IFFLAG, "ipairs"},
|
||||||
"tonumber", "tostring", "type", "xpcall", NULL
|
{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 */
|
/* NHL_BASE_ERROR - not really safe - might not want Lua to kill the process */
|
||||||
static const char *ct_base_error[] = {
|
static struct e ct_base_error[] = {
|
||||||
"assert", /* ok, calls error */
|
{IFFLAG, "assert"}, /* ok, calls error */
|
||||||
"error", /* ok, calls G->panic */
|
{IFFLAG, "error"}, /* ok, calls G->panic */
|
||||||
/* "print", not ok - calls lua_writestring/lua_writeline -> stdout*/
|
{NEVER, "print"}, /*not ok - calls lua_writestring/lua_writeline -> stdout*/
|
||||||
/* "warn", not ok - calls lua_writestringerror -> stderr */
|
{NEVER, "warn"}, /*not ok - calls lua_writestringerror -> stderr*/
|
||||||
NULL
|
{EOT, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* NHL_BASE_META - metatable access */
|
/* NHL_BASE_META - metatable access */
|
||||||
static const char *ct_base_meta[] = {
|
static struct e ct_base_meta[] = {
|
||||||
"getmetatable", "rawget", "rawset", "setmetatable", NULL
|
{IFFLAG, "getmetatable"},
|
||||||
|
{IFFLAG, "rawequal"},
|
||||||
|
{IFFLAG, "rawget"},
|
||||||
|
{IFFLAG, "rawlen"},
|
||||||
|
{IFFLAG, "rawset"},
|
||||||
|
{IFFLAG, "setmetatable"},
|
||||||
|
{EOT, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* NHL_BASE_GC - questionable safety */
|
/* NHL_BASE_GC - questionable safety */
|
||||||
static const char *ct_base_iffy[] = {
|
static struct e ct_base_iffy[] = {
|
||||||
"collectgarbage", NULL
|
{IFFLAG, "collectgarbage"},
|
||||||
|
{EOT, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* NHL_BASE_UNSAFE - include only if required */
|
/* NHL_BASE_UNSAFE - include only if required */
|
||||||
static const char *ct_base_unsafe[] = {
|
static struct e ct_base_unsafe[] = {
|
||||||
"dofile", "loadfile", "load", NULL
|
{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
|
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_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
|
#endif
|
||||||
@@ -2014,27 +2099,14 @@ DISABLE_WARNING_CONDEXPR_IS_CONSTANT
|
|||||||
#ifdef NHL_SANDBOX
|
#ifdef NHL_SANDBOX
|
||||||
static void
|
static void
|
||||||
nhlL_openlibs(lua_State *L, uint32_t lflags){
|
nhlL_openlibs(lua_State *L, uint32_t lflags){
|
||||||
uint32_t needbase;
|
|
||||||
|
|
||||||
/* translate lflags from user-friendly to internal */
|
/* translate lflags from user-friendly to internal */
|
||||||
if (NHL_SB_DEBUGGING & lflags){
|
if (NHL_SB_DEBUGGING & lflags){
|
||||||
#if 1 /* XXX */
|
lflags |= NHL_SB_DB_SAFE;
|
||||||
lflags |= NHL_SB_DB;
|
|
||||||
/* XXX
|
|
||||||
Should these be available as safe or as a low level group?
|
|
||||||
debug.getinfo
|
|
||||||
debug.traceback?
|
|
||||||
*/
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
/* only for debugging the sandbox integration */
|
/* only for debugging the sandbox integration */
|
||||||
if (NHL_SB_ALL & lflags){
|
if (NHL_SB_ALL & lflags){
|
||||||
lflags = -1;
|
lflags = -1;
|
||||||
} else if ((NHL_SB_SAFE
|
} else if (NHL_SB_SAFE & lflags){
|
||||||
#ifdef notyet
|
|
||||||
|NHL_SB_CANREAD
|
|
||||||
#endif
|
|
||||||
) & lflags){
|
|
||||||
lflags |= NHL_SB_BASE_BASE;
|
lflags |= NHL_SB_BASE_BASE;
|
||||||
lflags |= NHL_SB_COROUTINE;
|
lflags |= NHL_SB_COROUTINE;
|
||||||
lflags |= NHL_SB_TABLE;
|
lflags |= NHL_SB_TABLE;
|
||||||
@@ -2043,30 +2115,22 @@ debug.traceback?
|
|||||||
lflags |= NHL_SB_UTF8;
|
lflags |= NHL_SB_UTF8;
|
||||||
} else if (NHL_SB_VERSION){
|
} else if (NHL_SB_VERSION){
|
||||||
lflags |= NHL_SB_BASE_BASE;
|
lflags |= NHL_SB_BASE_BASE;
|
||||||
|
}
|
||||||
#ifdef notyet
|
#ifdef notyet
|
||||||
} else if (NHL_SB_CANREAD & lflags){
|
/* Handling I/O is complex, so it's not available yet. I'll
|
||||||
/* QQQ */
|
finish it if and when we need it. (keni)
|
||||||
/*
|
|
||||||
canread may be wrong.
|
|
||||||
How about:
|
|
||||||
- sets of fns (as below, as base)
|
|
||||||
- hooked open; array of tuples of (r/w/rw/a/etc, directory pat, file pat)
|
- 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
|
{"close", io_close}, but with no args closes default output, so needs hook
|
||||||
{"flush", io_flush},
|
{"flush", io_flush},
|
||||||
{"lines", io_lines}, hook due to filename
|
{"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"
|
only safe if mode not present or == "r"
|
||||||
or WRITEIO
|
or WRITEIO
|
||||||
only safe if path has no slashes
|
only safe if path has no slashes
|
||||||
XXX probably need to be: matches port-specific list of paths
|
XXX probably need to be: matches port-specific list of paths
|
||||||
WRITEIO needs a different list
|
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 l_getc (but that wouldn't hook core)
|
||||||
may need to #define fopen/fread/fwrite/feof/ftell (etc?)
|
may need to #define fopen/fread/fwrite/feof/ftell (etc?)
|
||||||
ugh: lauxlib.c uses getc() below luaL_loadfilex
|
ugh: lauxlib.c uses getc() below luaL_loadfilex
|
||||||
@@ -2084,39 +2148,24 @@ UNSAFEIO:
|
|||||||
{"tmpfile", io_tmpfile},
|
{"tmpfile", io_tmpfile},
|
||||||
*/
|
*/
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
if(lflags & NHL_SB_BASEMASK){
|
||||||
multiple levels - io.*, FILE.* - can we hook FILE.*?
|
int baselib;
|
||||||
see liolib.c:{meta, createmeta, luaopen_io}
|
/* load the entire library ... */
|
||||||
// do we need anything else? meta?
|
|
||||||
*/
|
|
||||||
|
|
||||||
needbase = lflags & NHL_SB_BASEMASK;
|
|
||||||
if(needbase){
|
|
||||||
luaL_requiref(L, LUA_GNAME, luaopen_base, 1);
|
luaL_requiref(L, LUA_GNAME, luaopen_base, 1);
|
||||||
int baselib = lua_gettop(L);
|
|
||||||
|
|
||||||
/* now remove everything not requested */
|
baselib = lua_gettop(L);
|
||||||
uint16_t rejectflags = ~lflags;
|
|
||||||
#define DROPIF(flag, x, table) \
|
|
||||||
if(rejectflags & flag){ nhl_clearfromtable(L, x, table); }
|
|
||||||
|
|
||||||
|
/* ... and remove anything unsupported or not requested */
|
||||||
DROPIF(NHL_SB_BASE_BASE, baselib, ct_base_base);
|
DROPIF(NHL_SB_BASE_BASE, baselib, ct_base_base);
|
||||||
DROPIF(NHL_SB_BASE_ERROR, baselib, ct_base_error);
|
DROPIF(NHL_SB_BASE_ERROR, baselib, ct_base_error);
|
||||||
DROPIF(NHL_SB_BASE_META, baselib, ct_base_meta);
|
DROPIF(NHL_SB_BASE_META, baselib, ct_base_meta);
|
||||||
DROPIF(NHL_SB_BASE_GC, baselib, ct_base_iffy);
|
DROPIF(NHL_SB_BASE_GC, baselib, ct_base_iffy);
|
||||||
DROPIF(NHL_SB_BASE_UNSAFE, baselib, ct_base_unsafe);
|
DROPIF(NHL_SB_BASE_UNSAFE, baselib, ct_base_unsafe);
|
||||||
#undef DROPIF
|
|
||||||
lua_pop(L, 1);
|
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){
|
if(lflags & NHL_SB_COROUTINE){
|
||||||
luaL_requiref(L, LUA_COLIBNAME, luaopen_coroutine, 1);
|
luaL_requiref(L, LUA_COLIBNAME, luaopen_coroutine, 1);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
@@ -2132,12 +2181,16 @@ multiple levels - io.*, FILE.* - can we hook FILE.*?
|
|||||||
if(!hook_open(L))
|
if(!hook_open(L))
|
||||||
panic("can't hook io.open");
|
panic("can't hook io.open");
|
||||||
}
|
}
|
||||||
// maybe ok: time, difftime, getenv clock date
|
#endif
|
||||||
if(lflags & NHL_SB_OS){
|
if(lflags & NHL_SB_OSMASK){
|
||||||
|
int oslib;
|
||||||
luaL_requiref(L, LUA_OSLIBNAME, luaopen_os, 1);
|
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);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
if(lflags & NHL_SB_STRING){
|
if(lflags & NHL_SB_STRING){
|
||||||
luaL_requiref(L, LUA_STRLIBNAME, luaopen_string, 1);
|
luaL_requiref(L, LUA_STRLIBNAME, luaopen_string, 1);
|
||||||
lua_pop(L, 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);
|
luaL_requiref(L, LUA_UTF8LIBNAME, luaopen_utf8, 1);
|
||||||
lua_pop(L, 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);
|
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);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user