From a50d07d39a76053328846d82a32bac61468bb16f Mon Sep 17 00:00:00 2001 From: red-001 Date: Sat, 28 Jan 2017 16:24:25 +0000 Subject: [CSM] Improve security for client-sided mods (#5100) --- src/script/cpp_api/s_security.cpp | 185 ++++++++++++++++++++++++++++++++------ src/script/cpp_api/s_security.h | 2 + 2 files changed, 162 insertions(+), 25 deletions(-) (limited to 'src/script/cpp_api') diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index f85cd0c9c..c6aad71b8 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -139,32 +139,8 @@ void ScriptApiSecurity::initializeSecurity() lua_State *L = getStack(); - // Backup globals to the registry - lua_getglobal(L, "_G"); - lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP); - - // Replace the global environment with an empty one -#if LUA_VERSION_NUM <= 501 - int is_main = lua_pushthread(L); // Push the main thread - FATAL_ERROR_IF(!is_main, "Security: ScriptApi's Lua state " - "isn't the main Lua thread!"); -#endif - lua_newtable(L); // Create new environment - lua_pushvalue(L, -1); - lua_setfield(L, -2, "_G"); // Set _G of new environment -#if LUA_VERSION_NUM >= 502 // Lua >= 5.2 - // Set the global environment - lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); -#else // Lua <= 5.1 - // Set the environment of the main thread - FATAL_ERROR_IF(!lua_setfenv(L, -2), "Security: Unable to set " - "environment of the main Lua thread!"); - lua_pop(L, 1); // Pop thread -#endif - // Get old globals - lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP); - int old_globals = lua_gettop(L); + int old_globals = backupGlobals(L); // Copy safe base functions @@ -223,7 +199,136 @@ void ScriptApiSecurity::initializeSecurity() lua_setglobal(L, "package"); lua_pop(L, 1); // Pop old package +#if USE_LUAJIT + // Copy safe jit functions, if they exist + lua_getfield(L, -1, "jit"); + if (!lua_isnil(L, -1)) { + lua_newtable(L); + copy_safe(L, jit_whitelist, sizeof(jit_whitelist)); + lua_setglobal(L, "jit"); + } + lua_pop(L, 1); // Pop old jit +#endif + + lua_pop(L, 1); // Pop globals_backup +} + +void ScriptApiSecurity::initializeSecurityClient() +{ + static const char *whitelist[] = { + "assert", + "core", + "collectgarbage", + "DIR_DELIM", + "error", + "getfenv", + "ipairs", + "next", + "pairs", + "pcall", + "print", + "rawequal", + "rawget", + "rawset", + "select", + "setfenv", + "setmetatable", + "tonumber", + "tostring", + "type", + "unpack", + "_VERSION", + "xpcall", + // Completely safe libraries + "coroutine", + "string", + "table", + "math", + }; + static const char *io_whitelist[] = { + "close", + "flush", + "read", + "type", + "write", + }; + static const char *os_whitelist[] = { + "clock", + "date", + "difftime", + "time", + "setlocale", + }; + static const char *debug_whitelist[] = { + "getinfo", + }; + static const char *jit_whitelist[] = { + "arch", + "flush", + "off", + "on", + "opt", + "os", + "status", + "version", + "version_num", + }; + + m_secure = true; + + lua_State *L = getStack(); + + + int old_globals = backupGlobals(L); + + + // Copy safe base functions + lua_getglobal(L, "_G"); + copy_safe(L, whitelist, sizeof(whitelist)); + + // And replace unsafe ones + SECURE_API(g, dofile); + SECURE_API(g, loadstring); + SECURE_API(g, require); + lua_pop(L, 1); + + + // Copy safe IO functions + lua_getfield(L, old_globals, "io"); + lua_newtable(L); + copy_safe(L, io_whitelist, sizeof(io_whitelist)); + + // And replace unsafe ones + SECURE_API(io, open); + SECURE_API(io, input); + SECURE_API(io, output); + SECURE_API(io, lines); + + lua_setglobal(L, "io"); + lua_pop(L, 1); // Pop old IO + + + // Copy safe OS functions + lua_getfield(L, old_globals, "os"); + lua_newtable(L); + copy_safe(L, os_whitelist, sizeof(os_whitelist)); + lua_setglobal(L, "os"); + lua_pop(L, 1); // Pop old OS + + + // Copy safe debug functions + lua_getfield(L, old_globals, "debug"); + lua_newtable(L); + copy_safe(L, debug_whitelist, sizeof(debug_whitelist)); + lua_setglobal(L, "debug"); + lua_pop(L, 1); // Pop old debug + + // Remove all of package + lua_newtable(L); + lua_setglobal(L, "package"); + +#if USE_LUAJIT // Copy safe jit functions, if they exist lua_getfield(L, -1, "jit"); if (!lua_isnil(L, -1)) { @@ -232,10 +337,40 @@ void ScriptApiSecurity::initializeSecurity() lua_setglobal(L, "jit"); } lua_pop(L, 1); // Pop old jit +#endif lua_pop(L, 1); // Pop globals_backup } +int ScriptApiSecurity::backupGlobals(lua_State *L) +{ + // Backup globals to the registry + lua_getglobal(L, "_G"); + lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP); + + // Replace the global environment with an empty one +#if LUA_VERSION_NUM <= 501 + int is_main = lua_pushthread(L); // Push the main thread + FATAL_ERROR_IF(!is_main, "Security: ScriptApi's Lua state " + "isn't the main Lua thread!"); +#endif + lua_newtable(L); // Create new environment + lua_pushvalue(L, -1); + lua_setfield(L, -2, "_G"); // Set _G of new environment +#if LUA_VERSION_NUM >= 502 // Lua >= 5.2 + // Set the global environment + lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); +#else // Lua <= 5.1 + // Set the environment of the main thread + FATAL_ERROR_IF(!lua_setfenv(L, -2), "Security: Unable to set " + "environment of the main Lua thread!"); + lua_pop(L, 1); // Pop thread +#endif + + // Get old globals + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP); + return lua_gettop(L); +} bool ScriptApiSecurity::isSecure(lua_State *L) { diff --git a/src/script/cpp_api/s_security.h b/src/script/cpp_api/s_security.h index 6876108e8..f0eef00bb 100644 --- a/src/script/cpp_api/s_security.h +++ b/src/script/cpp_api/s_security.h @@ -41,8 +41,10 @@ with this program; if not, write to the Free Software Foundation, Inc., class ScriptApiSecurity : virtual public ScriptApiBase { public: + int backupGlobals(lua_State *L); // Sets up security on the ScriptApi's Lua state void initializeSecurity(); + void initializeSecurityClient(); // Checks if the Lua state has been secured static bool isSecure(lua_State *L); // Loads a file as Lua code safely (doesn't allow bytecode). -- cgit v1.2.3