Require request_insecure_environment to be called from the mod's main scope
authorShadowNinja <shadowninja@minetest.net>
Thu, 18 Feb 2016 21:06:07 +0000 (16:06 -0500)
committerparamat <mat.gregory@virginmedia.com>
Fri, 19 Feb 2016 14:52:10 +0000 (14:52 +0000)
Previously you could steal a secure environment from a trusted mod by wrapping
request_insecure_environment with some code like this:

local rie_cp = minetest.request_insecure_environment
local stolen_ie
function minetest.request_insecure_environment()
local ie = rie_cp()
stolen_ie = stolen_ie or ie
return ie
end

doc/lua_api.txt
src/script/lua_api/l_util.cpp

index d9a8bea979fd320173ef0ca22a986970fc50d7da..2df0cac7c676d838cf8355df5ec567c1239d036c 100644 (file)
@@ -2437,7 +2437,7 @@ These functions return the leftover itemstack.
 * `minetest.request_insecure_environment()`: returns an environment containing
   insecure functions if the calling mod has been listed as trusted in the
   `secure.trusted_mods` setting or security is disabled, otherwise returns `nil`.
-    * Only works at init time.
+    * Only works at init time and must be called from the mod's main scope (not from a function).
     * **DO NOT ALLOW ANY OTHER MODS TO ACCESS THE RETURNED ENVIRONMENT, STORE IT IN
       A LOCAL VARIABLE!**
 
index 39863b987a7dea893847519e727fcd864f19b463..c1e883a98c29902734998709a5997bfe288ea001 100644 (file)
@@ -357,22 +357,44 @@ int ModApiUtil::l_get_dir_list(lua_State *L)
 int ModApiUtil::l_request_insecure_environment(lua_State *L)
 {
        NO_MAP_LOCK_REQUIRED;
+
+       // Just return _G if security is disabled
        if (!ScriptApiSecurity::isSecure(L)) {
                lua_getglobal(L, "_G");
                return 1;
        }
+
+       // We have to make sure that this function is being called directly by
+       // a mod, otherwise a malicious mod could override this function and
+       // steal its return value.
+       lua_Debug info;
+       // Make sure there's only one item below this function on the stack...
+       if (lua_getstack(L, 2, &info)) {
+               return 0;
+       }
+       assert(lua_getstack(L, 1, &info));
+       assert(lua_getinfo(L, "S", &info));
+       // ...and that that item is the main file scope.
+       if (strcmp(info.what, "main") != 0) {
+               return 0;
+       }
+
+       // Get mod name
        lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
        if (!lua_isstring(L, -1)) {
-               lua_pushnil(L);
-               return 1;
+               return 0;
        }
+
+       // Check secure.trusted_mods
        const char *mod_name = lua_tostring(L, -1);
        std::string trusted_mods = g_settings->get("secure.trusted_mods");
        std::vector<std::string> mod_list = str_split(trusted_mods, ',');
-       if (std::find(mod_list.begin(), mod_list.end(), mod_name) == mod_list.end()) {
-               lua_pushnil(L);
-               return 1;
+       if (std::find(mod_list.begin(), mod_list.end(), mod_name) ==
+                       mod_list.end()) {
+               return 0;
        }
+
+       // Push insecure environment
        lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
        return 1;
 }