diff options
Diffstat (limited to 'src/script/cpp_api/s_security.cpp')
-rw-r--r-- | src/script/cpp_api/s_security.cpp | 360 |
1 files changed, 189 insertions, 171 deletions
diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index 21bc7eb0a..9d65819c0 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -29,20 +29,19 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <string> #include <iostream> -#define SECURE_API(lib, name) \ - lua_pushcfunction(L, sl_##lib##_##name); \ + +#define SECURE_API(lib, name) \ + lua_pushcfunction(L, sl_##lib##_##name); \ lua_setfield(L, -2, #name); -static inline void copy_safe(lua_State *L, const char *list[], unsigned len, - int from = -2, int to = -1) + +static inline void copy_safe(lua_State *L, const char *list[], unsigned len, int from=-2, int to=-1) { - if (from < 0) - from = lua_gettop(L) + from + 1; - if (to < 0) - to = lua_gettop(L) + to + 1; + if (from < 0) from = lua_gettop(L) + from + 1; + if (to < 0) to = lua_gettop(L) + to + 1; for (unsigned i = 0; i < (len / sizeof(list[0])); i++) { lua_getfield(L, from, list[i]); - lua_setfield(L, to, list[i]); + lua_setfield(L, to, list[i]); } } @@ -51,90 +50,91 @@ static inline void push_original(lua_State *L, const char *lib, const char *func { lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP); lua_getfield(L, -1, lib); - lua_remove(L, -2); // Remove globals_backup + lua_remove(L, -2); // Remove globals_backup lua_getfield(L, -1, func); - lua_remove(L, -2); // Remove lib + lua_remove(L, -2); // Remove lib } + void ScriptApiSecurity::initializeSecurity() { static const char *whitelist[] = { - "assert", - "core", - "collectgarbage", - "DIR_DELIM", - "error", - "getfenv", - "getmetatable", - "ipairs", - "next", - "pairs", - "pcall", - "print", - "rawequal", - "rawget", - "rawset", - "select", - "setfenv", - "setmetatable", - "tonumber", - "tostring", - "type", - "unpack", - "_VERSION", - "xpcall", - // Completely safe libraries - "coroutine", - "string", - "table", - "math", + "assert", + "core", + "collectgarbage", + "DIR_DELIM", + "error", + "getfenv", + "getmetatable", + "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[] = { - "open", - "close", - "flush", - "read", - "type", - "write", + "open", + "close", + "flush", + "read", + "type", + "write", }; static const char *os_whitelist[] = { - "clock", - "date", - "difftime", - "getenv", - "setlocale", - "time", - "tmpname", + "clock", + "date", + "difftime", + "getenv", + "setlocale", + "time", + "tmpname", }; static const char *debug_whitelist[] = { - "gethook", - "traceback", - "getinfo", - "getmetatable", - "setupvalue", - "setmetatable", - "upvalueid", - "sethook", - "debug", - "setlocal", + "gethook", + "traceback", + "getinfo", + "getmetatable", + "setupvalue", + "setmetatable", + "upvalueid", + "sethook", + "debug", + "setlocal", }; static const char *package_whitelist[] = { - "config", - "cpath", - "path", - "searchpath", + "config", + "cpath", + "path", + "searchpath", }; #if USE_LUAJIT static const char *jit_whitelist[] = { - "arch", - "flush", - "off", - "on", - "opt", - "os", - "status", - "version", - "version_num", + "arch", + "flush", + "off", + "on", + "opt", + "os", + "status", + "version", + "version_num", }; #endif m_secure = true; @@ -154,6 +154,7 @@ void ScriptApiSecurity::initializeSecurity() lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP); int old_globals = lua_gettop(L); + // Copy safe base functions lua_getglobal(L, "_G"); copy_safe(L, whitelist, sizeof(whitelist)); @@ -166,19 +167,21 @@ void ScriptApiSecurity::initializeSecurity() 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, open); SECURE_API(io, input); SECURE_API(io, output); SECURE_API(io, lines); lua_setglobal(L, "io"); - lua_pop(L, 1); // Pop old IO + lua_pop(L, 1); // Pop old IO + // Copy safe OS functions lua_getfield(L, old_globals, "os"); @@ -190,21 +193,23 @@ void ScriptApiSecurity::initializeSecurity() SECURE_API(os, rename); lua_setglobal(L, "os"); - lua_pop(L, 1); // Pop old 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 + lua_pop(L, 1); // Pop old debug + // Copy safe package fields lua_getfield(L, old_globals, "package"); lua_newtable(L); copy_safe(L, package_whitelist, sizeof(package_whitelist)); lua_setglobal(L, "package"); - lua_pop(L, 1); // Pop old package + lua_pop(L, 1); // Pop old package #if USE_LUAJIT // Copy safe jit functions, if they exist @@ -214,7 +219,7 @@ void ScriptApiSecurity::initializeSecurity() copy_safe(L, jit_whitelist, sizeof(jit_whitelist)); lua_setglobal(L, "jit"); } - lua_pop(L, 1); // Pop old jit + lua_pop(L, 1); // Pop old jit #endif lua_pop(L, 1); // Pop globals_backup @@ -223,49 +228,57 @@ void ScriptApiSecurity::initializeSecurity() void ScriptApiSecurity::initializeSecurityClient() { static const char *whitelist[] = { - "assert", - "core", - "collectgarbage", - "DIR_DELIM", - "error", - "getfenv", - "ipairs", - "next", - "pairs", - "pcall", - "print", - "rawequal", - "rawget", - "rawset", - "select", - "setfenv", - // getmetatable can be used to escape the sandbox - "setmetatable", - "tonumber", - "tostring", - "type", - "unpack", - "_VERSION", - "xpcall", - // Completely safe libraries - "coroutine", - "string", - "table", - "math", + "assert", + "core", + "collectgarbage", + "DIR_DELIM", + "error", + "getfenv", + "ipairs", + "next", + "pairs", + "pcall", + "print", + "rawequal", + "rawget", + "rawset", + "select", + "setfenv", + // getmetatable can be used to escape the sandbox + "setmetatable", + "tonumber", + "tostring", + "type", + "unpack", + "_VERSION", + "xpcall", + // Completely safe libraries + "coroutine", + "string", + "table", + "math", + }; + static const char *os_whitelist[] = { + "clock", + "date", + "difftime", + "time" + }; + static const char *debug_whitelist[] = { + "getinfo", + "traceback" }; - static const char *os_whitelist[] = {"clock", "date", "difftime", "time"}; - static const char *debug_whitelist[] = {"getinfo", "traceback"}; #if USE_LUAJIT static const char *jit_whitelist[] = { - "arch", - "flush", - "off", - "on", - "opt", - "os", - "status", - "version", - "version_num", + "arch", + "flush", + "off", + "on", + "opt", + "os", + "status", + "version", + "version_num", }; #endif @@ -299,14 +312,16 @@ void ScriptApiSecurity::initializeSecurityClient() lua_newtable(L); copy_safe(L, os_whitelist, sizeof(os_whitelist)); lua_setfield(L, -3, "os"); - lua_pop(L, 1); // Pop old OS + lua_pop(L, 1); // Pop old OS + // Copy safe debug functions lua_getglobal(L, "debug"); lua_newtable(L); copy_safe(L, debug_whitelist, sizeof(debug_whitelist)); lua_setfield(L, -3, "debug"); - lua_pop(L, 1); // Pop old debug + lua_pop(L, 1); // Pop old debug + #if USE_LUAJIT // Copy safe jit functions, if they exist @@ -314,7 +329,7 @@ void ScriptApiSecurity::initializeSecurityClient() lua_newtable(L); copy_safe(L, jit_whitelist, sizeof(jit_whitelist)); lua_setfield(L, -3, "jit"); - lua_pop(L, 1); // Pop old jit + lua_pop(L, 1); // Pop old jit #endif // Set the environment to the one we created earlier @@ -324,9 +339,9 @@ void ScriptApiSecurity::initializeSecurityClient() int ScriptApiSecurity::getThread(lua_State *L) { #if LUA_VERSION_NUM <= 501 - int is_main = lua_pushthread(L); // Push the main thread + 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!"); + "isn't the main Lua thread!"); return lua_gettop(L); #endif return 0; @@ -334,21 +349,21 @@ int ScriptApiSecurity::getThread(lua_State *L) void ScriptApiSecurity::createEmptyEnv(lua_State *L) { - lua_newtable(L); // Create new environment + lua_newtable(L); // Create new environment lua_pushvalue(L, -1); - lua_setfield(L, -2, "_G"); // Create the _G loop + lua_setfield(L, -2, "_G"); // Create the _G loop } void ScriptApiSecurity::setLuaEnv(lua_State *L, int thread) { -#if LUA_VERSION_NUM >= 502 // Lua >= 5.2 +#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 +#else // Lua <= 5.1 + // Set the environment of the main thread FATAL_ERROR_IF(!lua_setfenv(L, thread), "Security: Unable to set " - "environment of the main Lua thread!"); - lua_pop(L, 1); // Pop thread + "environment of the main Lua thread!"); + lua_pop(L, 1); // Pop thread #endif } @@ -360,8 +375,7 @@ bool ScriptApiSecurity::isSecure(lua_State *L) return secure; } -bool ScriptApiSecurity::safeLoadString( - lua_State *L, const std::string &code, const char *chunk_name) +bool ScriptApiSecurity::safeLoadString(lua_State *L, const std::string &code, const char *chunk_name) { if (code.size() > 0 && code[0] == LUA_SIGNATURE[0]) { lua_pushliteral(L, "Bytecode prohibited when mod security is enabled."); @@ -372,8 +386,7 @@ bool ScriptApiSecurity::safeLoadString( return true; } -bool ScriptApiSecurity::safeLoadFile( - lua_State *L, const char *path, const char *display_name) +bool ScriptApiSecurity::safeLoadFile(lua_State *L, const char *path, const char *display_name) { FILE *fp; char *chunk_name; @@ -398,8 +411,7 @@ bool ScriptApiSecurity::safeLoadFile( int c = std::getc(fp); if (c == '#') { // Skip the first line - while ((c = std::getc(fp)) != EOF && c != '\n') { - } + while ((c = std::getc(fp)) != EOF && c != '\n') {} if (c == '\n') std::getc(fp); start = std::ftell(fp); @@ -411,7 +423,7 @@ bool ScriptApiSecurity::safeLoadFile( lua_pushfstring(L, "%s: %s", path, strerror(errno)); if (path) { std::fclose(fp); - delete[] chunk_name; + delete [] chunk_name; } return false; } @@ -423,7 +435,7 @@ bool ScriptApiSecurity::safeLoadFile( lua_pushfstring(L, "%s: %s", path, strerror(errno)); if (path) { std::fclose(fp); - delete[] chunk_name; + delete [] chunk_name; } return false; } @@ -434,31 +446,31 @@ bool ScriptApiSecurity::safeLoadFile( if (num_read != size) { lua_pushliteral(L, "Error reading file to load."); if (path) - delete[] chunk_name; + delete [] chunk_name; return false; } bool result = safeLoadString(L, code, chunk_name); if (path) - delete[] chunk_name; + delete [] chunk_name; return result; } -bool ScriptApiSecurity::checkPath( - lua_State *L, const char *path, bool write_required, bool *write_allowed) + +bool ScriptApiSecurity::checkPath(lua_State *L, const char *path, + bool write_required, bool *write_allowed) { if (write_allowed) *write_allowed = false; - std::string str; // Transient + std::string str; // Transient std::string abs_path = fs::AbsolutePath(path); if (!abs_path.empty()) { // Don't allow accessing the settings file str = fs::AbsolutePath(g_settings_path); - if (str == abs_path) - return false; + if (str == abs_path) return false; } // If we couldn't find the absolute path (path doesn't exist) then @@ -470,18 +482,15 @@ bool ScriptApiSecurity::checkPath( std::string component; cur_path = fs::RemoveLastPathComponent(cur_path, &component); if (component == "..") { - // Parent components can't be allowed or we could allow something - // like + // Parent components can't be allowed or we could allow something like // /home/user/minetest/worlds/foo/noexist/../../../../../../etc/passwd. - // If we have previous non-relative elements in the path we might - // be able to remove them so that things like - // worlds/foo/noexist/../auth.txt could be allowed, but those - // paths will be interpreted as nonexistent by the operating - // system anyways. + // If we have previous non-relative elements in the path we might be + // able to remove them so that things like worlds/foo/noexist/../auth.txt + // could be allowed, but those paths will be interpreted as nonexistent + // by the operating system anyways. return false; } - removed.append(component).append( - removed.empty() ? "" : DIR_DELIM + removed); + removed.append(component).append(removed.empty() ? "" : DIR_DELIM + removed); abs_path = fs::AbsolutePath(cur_path); } if (abs_path.empty()) @@ -495,9 +504,9 @@ bool ScriptApiSecurity::checkPath( lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI); ScriptApiBase *script; #if INDIRECT_SCRIPTAPI_RIDX - script = (ScriptApiBase *)*(void **)(lua_touserdata(L, -1)); + script = (ScriptApiBase *) *(void**)(lua_touserdata(L, -1)); #else - script = (ScriptApiBase *)lua_touserdata(L, -1); + script = (ScriptApiBase *) lua_touserdata(L, -1); #endif lua_pop(L, 1); const IGameDef *gamedef = script->getGameDef(); @@ -511,27 +520,24 @@ bool ScriptApiSecurity::checkPath( // Builtin can access anything if (mod_name == BUILTIN_MOD_NAME) { - if (write_allowed) - *write_allowed = true; + if (write_allowed) *write_allowed = true; return true; } // Allow paths in mod path - // Don't bother if write access isn't important, since it will be handled - // later + // Don't bother if write access isn't important, since it will be handled later if (write_required || write_allowed != NULL) { const ModSpec *mod = gamedef->getModSpec(mod_name); if (mod) { str = fs::AbsolutePath(mod->path); if (!str.empty() && fs::PathStartsWith(abs_path, str)) { - if (write_allowed) - *write_allowed = true; + if (write_allowed) *write_allowed = true; return true; } } } } - lua_pop(L, 1); // Pop mod name + lua_pop(L, 1); // Pop mod name // Allow read-only access to all mod directories if (!write_required) { @@ -558,8 +564,7 @@ bool ScriptApiSecurity::checkPath( } // Allow all other paths in world path if (fs::PathStartsWith(abs_path, str)) { - if (write_allowed) - *write_allowed = true; + if (write_allowed) *write_allowed = true; return true; } } @@ -568,6 +573,7 @@ bool ScriptApiSecurity::checkPath( return false; } + int ScriptApiSecurity::sl_g_dofile(lua_State *L) { int nret = sl_g_loadfile(L); @@ -582,6 +588,7 @@ int ScriptApiSecurity::sl_g_dofile(lua_State *L) return lua_gettop(L) - (top_precall - 1); } + int ScriptApiSecurity::sl_g_load(lua_State *L) { size_t len; @@ -620,11 +627,12 @@ int ScriptApiSecurity::sl_g_load(lua_State *L) return 1; } + int ScriptApiSecurity::sl_g_loadfile(lua_State *L) { #ifndef SERVER lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI); - ScriptApiBase *script = (ScriptApiBase *)lua_touserdata(L, -1); + ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1); lua_pop(L, 1); // Client implementation @@ -664,6 +672,7 @@ int ScriptApiSecurity::sl_g_loadfile(lua_State *L) return 1; } + int ScriptApiSecurity::sl_g_loadstring(lua_State *L) { const char *chunk_name = "=(load)"; @@ -686,12 +695,14 @@ int ScriptApiSecurity::sl_g_loadstring(lua_State *L) return 1; } + int ScriptApiSecurity::sl_g_require(lua_State *L) { lua_pushliteral(L, "require() is disabled when mod security is on."); return lua_error(L); } + int ScriptApiSecurity::sl_io_open(lua_State *L) { bool with_mode = lua_gettop(L) > 1; @@ -704,7 +715,8 @@ int ScriptApiSecurity::sl_io_open(lua_State *L) luaL_checktype(L, 2, LUA_TSTRING); const char *mode = lua_tostring(L, 2); write_requested = strchr(mode, 'w') != NULL || - strchr(mode, '+') != NULL || strchr(mode, 'a') != NULL; + strchr(mode, '+') != NULL || + strchr(mode, 'a') != NULL; } CHECK_SECURE_PATH_INTERNAL(L, path, write_requested, NULL); @@ -718,6 +730,7 @@ int ScriptApiSecurity::sl_io_open(lua_State *L) return 2; } + int ScriptApiSecurity::sl_io_input(lua_State *L) { if (lua_isstring(L, 1)) { @@ -731,6 +744,7 @@ int ScriptApiSecurity::sl_io_input(lua_State *L) return 1; } + int ScriptApiSecurity::sl_io_output(lua_State *L) { if (lua_isstring(L, 1)) { @@ -744,6 +758,7 @@ int ScriptApiSecurity::sl_io_output(lua_State *L) return 1; } + int ScriptApiSecurity::sl_io_lines(lua_State *L) { if (lua_isstring(L, 1)) { @@ -760,6 +775,7 @@ int ScriptApiSecurity::sl_io_lines(lua_State *L) return lua_gettop(L) - top_precall; } + int ScriptApiSecurity::sl_os_rename(lua_State *L) { luaL_checktype(L, 1, LUA_TSTRING); @@ -777,6 +793,7 @@ int ScriptApiSecurity::sl_os_rename(lua_State *L) return 2; } + int ScriptApiSecurity::sl_os_remove(lua_State *L) { luaL_checktype(L, 1, LUA_TSTRING); @@ -788,3 +805,4 @@ int ScriptApiSecurity::sl_os_remove(lua_State *L) lua_call(L, 1, 2); return 2; } + |