diff options
Diffstat (limited to 'src/script/cpp_api')
-rw-r--r-- | src/script/cpp_api/CMakeLists.txt | 8 | ||||
-rw-r--r-- | src/script/cpp_api/s_async.cpp | 15 | ||||
-rw-r--r-- | src/script/cpp_api/s_base.cpp | 156 | ||||
-rw-r--r-- | src/script/cpp_api/s_base.h | 39 | ||||
-rw-r--r-- | src/script/cpp_api/s_entity.cpp | 35 | ||||
-rw-r--r-- | src/script/cpp_api/s_env.cpp | 6 | ||||
-rw-r--r-- | src/script/cpp_api/s_internal.h | 1 | ||||
-rw-r--r-- | src/script/cpp_api/s_inventory.cpp | 21 | ||||
-rw-r--r-- | src/script/cpp_api/s_item.cpp | 20 | ||||
-rw-r--r-- | src/script/cpp_api/s_mainmenu.cpp | 22 | ||||
-rw-r--r-- | src/script/cpp_api/s_mainmenu.h | 15 | ||||
-rw-r--r-- | src/script/cpp_api/s_node.cpp | 34 | ||||
-rw-r--r-- | src/script/cpp_api/s_node.h | 5 | ||||
-rw-r--r-- | src/script/cpp_api/s_nodemeta.cpp | 24 | ||||
-rw-r--r-- | src/script/cpp_api/s_player.cpp | 79 | ||||
-rw-r--r-- | src/script/cpp_api/s_player.h | 16 | ||||
-rw-r--r-- | src/script/cpp_api/s_security.cpp | 604 | ||||
-rw-r--r-- | src/script/cpp_api/s_security.h | 70 | ||||
-rw-r--r-- | src/script/cpp_api/s_server.cpp | 16 |
19 files changed, 990 insertions, 196 deletions
diff --git a/src/script/cpp_api/CMakeLists.txt b/src/script/cpp_api/CMakeLists.txt index c45020055..be4d0131e 100644 --- a/src/script/cpp_api/CMakeLists.txt +++ b/src/script/cpp_api/CMakeLists.txt @@ -1,5 +1,5 @@ -# Used by server and client set(common_SCRIPT_CPP_API_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/s_async.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_base.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_entity.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_env.cpp @@ -8,11 +8,11 @@ set(common_SCRIPT_CPP_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/s_node.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_nodemeta.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_player.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/s_security.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_server.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/s_async.cpp PARENT_SCOPE) -# Used by client only -set(minetest_SCRIPT_CPP_API_SRCS +set(client_SCRIPT_CPP_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/s_mainmenu.cpp PARENT_SCOPE) + diff --git a/src/script/cpp_api/s_async.cpp b/src/script/cpp_api/s_async.cpp index de1ebc07b..c00b22f98 100644 --- a/src/script/cpp_api/s_async.cpp +++ b/src/script/cpp_api/s_async.cpp @@ -155,7 +155,7 @@ void AsyncEngine::step(lua_State *L, int errorhandler) lua_getfield(L, -1, "async_event_handler"); if (lua_isnil(L, -1)) { - assert("Async event handler does not exist!" == 0); + FATAL_ERROR("Async event handler does not exist!"); } luaL_checktype(L, -1, LUA_TFUNCTION); @@ -164,9 +164,7 @@ void AsyncEngine::step(lua_State *L, int errorhandler) lua_pushlstring(L, jobDone.serializedResult.data(), jobDone.serializedResult.size()); - if (lua_pcall(L, 2, 0, errorhandler)) { - script_error(L); - } + PCALL_RESL(L, lua_pcall(L, 2, 0, errorhandler)); } resultQueueMutex.Unlock(); lua_pop(L, 1); // Pop core @@ -237,7 +235,7 @@ AsyncWorkerThread::AsyncWorkerThread(AsyncEngine* jobDispatcher, /******************************************************************************/ AsyncWorkerThread::~AsyncWorkerThread() { - assert(IsRunning() == false); + sanity_check(IsRunning() == false); } /******************************************************************************/ @@ -257,7 +255,7 @@ void* AsyncWorkerThread::Thread() std::string script = getServer()->getBuiltinLuaPath() + DIR_DELIM + "init.lua"; if (!loadScript(script)) { errorstream - << "AsyncWorkderThread execution of async base environment failed!" + << "AsyncWorkerThread execution of async base environment failed!" << std::endl; abort(); } @@ -293,8 +291,9 @@ void* AsyncWorkerThread::Thread() toProcess.serializedParams.data(), toProcess.serializedParams.size()); - if (lua_pcall(L, 2, 1, m_errorhandler)) { - scriptError(); + int result = lua_pcall(L, 2, 1, m_errorhandler); + if (result) { + PCALL_RES(result); toProcess.serializedResult = ""; } else { // Fetch result diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index 1f96373dc..dcfbac4bf 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -19,7 +19,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_base.h" #include "cpp_api/s_internal.h" +#include "cpp_api/s_security.h" #include "lua_api/l_object.h" +#include "common/c_converter.h" #include "serverobject.h" #include "debug.h" #include "filesys.h" @@ -45,18 +47,18 @@ class ModNameStorer private: lua_State *L; public: - ModNameStorer(lua_State *L_, const std::string &modname): + ModNameStorer(lua_State *L_, const std::string &mod_name): L(L_) { - // Store current modname in registry - lua_pushstring(L, modname.c_str()); - lua_setfield(L, LUA_REGISTRYINDEX, "current_modname"); + // Store current mod name in registry + lua_pushstring(L, mod_name.c_str()); + lua_setfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD); } ~ModNameStorer() { - // Clear current modname in registry + // Clear current mod name from registry lua_pushnil(L); - lua_setfield(L, LUA_REGISTRYINDEX, "current_modname"); + lua_setfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD); } }; @@ -67,12 +69,12 @@ public: ScriptApiBase::ScriptApiBase() { - #ifdef SCRIPTAPI_LOCK_DEBUG +#ifdef SCRIPTAPI_LOCK_DEBUG m_locked = false; - #endif +#endif m_luastack = luaL_newstate(); - assert(m_luastack); + FATAL_ERROR_IF(!m_luastack, "luaL_newstate() failed"); luaL_openlibs(m_luastack); @@ -102,6 +104,11 @@ ScriptApiBase::ScriptApiBase() lua_pushstring(m_luastack, porting::getPlatformName()); lua_setglobal(m_luastack, "PLATFORM"); + // m_secure gets set to true inside + // ScriptApiSecurity::initializeSecurity(), if neccessary. + // Default to false otherwise + m_secure = false; + m_server = NULL; m_environment = NULL; m_guiengine = NULL; @@ -112,88 +119,136 @@ ScriptApiBase::~ScriptApiBase() lua_close(m_luastack); } -bool ScriptApiBase::loadMod(const std::string &scriptpath, - const std::string &modname) +bool ScriptApiBase::loadMod(const std::string &script_path, + const std::string &mod_name, std::string *error) { - ModNameStorer modnamestorer(getStack(), modname); - - if (!string_allowed(modname, MODNAME_ALLOWED_CHARS)) { - errorstream<<"Error loading mod \""<<modname - <<"\": modname does not follow naming conventions: " - <<"Only chararacters [a-z0-9_] are allowed."<<std::endl; - return false; - } + ModNameStorer mod_name_storer(getStack(), mod_name); - return loadScript(scriptpath); + return loadScript(script_path, error); } -bool ScriptApiBase::loadScript(const std::string &scriptpath) +bool ScriptApiBase::loadScript(const std::string &script_path, std::string *error) { - verbosestream<<"Loading and running script from "<<scriptpath<<std::endl; + verbosestream << "Loading and running script from " << script_path << std::endl; lua_State *L = getStack(); - int ret = luaL_loadfile(L, scriptpath.c_str()) || lua_pcall(L, 0, 0, m_errorhandler); - if (ret) { - errorstream << "========== ERROR FROM LUA ===========" << std::endl; - errorstream << "Failed to load and run script from " << std::endl; - errorstream << scriptpath << ":" << std::endl; - errorstream << std::endl; - errorstream << lua_tostring(L, -1) << std::endl; - errorstream << std::endl; - errorstream << "======= END OF ERROR FROM LUA ========" << std::endl; + bool ok; + if (m_secure) { + ok = ScriptApiSecurity::safeLoadFile(L, script_path.c_str()); + } else { + ok = !luaL_loadfile(L, script_path.c_str()); + } + ok = ok && !lua_pcall(L, 0, 0, m_errorhandler); + if (!ok) { + std::string error_msg = lua_tostring(L, -1); + if (error) + *error = error_msg; + errorstream << "========== ERROR FROM LUA ===========" << std::endl + << "Failed to load and run script from " << std::endl + << script_path << ":" << std::endl << std::endl + << error_msg << std::endl << std::endl + << "======= END OF ERROR FROM LUA ========" << std::endl; lua_pop(L, 1); // Pop error message from stack return false; } return true; } +// Push the list of callbacks (a lua table). +// Then push nargs arguments. +// Then call this function, which +// - runs the callbacks +// - replaces the table and arguments with the return value, +// computed depending on mode +void ScriptApiBase::runCallbacksRaw(int nargs, + RunCallbacksMode mode, const char *fxn) +{ + lua_State *L = getStack(); + FATAL_ERROR_IF(lua_gettop(L) < nargs + 1, "Not enough arguments"); + + // Insert error handler + lua_pushcfunction(L, script_error_handler); + int errorhandler = lua_gettop(L) - nargs - 1; + lua_insert(L, errorhandler); + + // Insert run_callbacks between error handler and table + lua_getglobal(L, "core"); + lua_getfield(L, -1, "run_callbacks"); + lua_remove(L, -2); + lua_insert(L, errorhandler + 1); + + // Insert mode after table + lua_pushnumber(L, (int)mode); + lua_insert(L, errorhandler + 3); + + // Stack now looks like this: + // ... <error handler> <run_callbacks> <table> <mode> <arg#1> <arg#2> ... <arg#n> + + int result = lua_pcall(L, nargs + 2, 1, errorhandler); + if (result != 0) + scriptError(result, fxn); + + lua_remove(L, -2); // Remove error handler +} + void ScriptApiBase::realityCheck() { int top = lua_gettop(m_luastack); - if(top >= 30){ - dstream<<"Stack is over 30:"<<std::endl; + if (top >= 30) { + dstream << "Stack is over 30:" << std::endl; stackDump(dstream); std::string traceback = script_get_backtrace(m_luastack); throw LuaError("Stack is over 30 (reality check)\n" + traceback); } } -void ScriptApiBase::scriptError() +void ScriptApiBase::scriptError(int result, const char *fxn) { - throw LuaError(lua_tostring(m_luastack, -1)); + script_error(getStack(), result, m_last_run_mod.c_str(), fxn); } void ScriptApiBase::stackDump(std::ostream &o) { - int i; int top = lua_gettop(m_luastack); - for (i = 1; i <= top; i++) { /* repeat for each level */ + for (int i = 1; i <= top; i++) { /* repeat for each level */ int t = lua_type(m_luastack, i); switch (t) { - case LUA_TSTRING: /* strings */ - o<<"\""<<lua_tostring(m_luastack, i)<<"\""; + o << "\"" << lua_tostring(m_luastack, i) << "\""; break; - case LUA_TBOOLEAN: /* booleans */ - o<<(lua_toboolean(m_luastack, i) ? "true" : "false"); + o << (lua_toboolean(m_luastack, i) ? "true" : "false"); break; - case LUA_TNUMBER: /* numbers */ { char buf[10]; snprintf(buf, 10, "%g", lua_tonumber(m_luastack, i)); - o<<buf; - break; } - + o << buf; + break; + } default: /* other values */ - o<<lua_typename(m_luastack, t); + o << lua_typename(m_luastack, t); break; - } - o<<" "; + o << " "; } - o<<std::endl; + o << std::endl; +} + +void ScriptApiBase::setOriginDirect(const char *origin) +{ + m_last_run_mod = origin ? origin : "??"; +} + +void ScriptApiBase::setOriginFromTableRaw(int index, const char *fxn) +{ +#ifdef SCRIPTAPI_DEBUG + lua_State *L = getStack(); + + m_last_run_mod = lua_istable(L, index) ? + getstringfield_default(L, index, "mod_origin", "") : ""; + //printf(">>>> running %s for mod: %s\n", fxn, m_last_run_mod.c_str()); +#endif } void ScriptApiBase::addObjectReference(ServerActiveObject *cobj) @@ -245,7 +300,7 @@ void ScriptApiBase::removeObjectReference(ServerActiveObject *cobj) void ScriptApiBase::objectrefGetOrCreate(lua_State *L, ServerActiveObject *cobj) { - if(cobj == NULL || cobj->getId() == 0){ + if (cobj == NULL || cobj->getId() == 0) { ObjectRef::create(L, cobj); } else { objectrefGet(L, cobj->getId()); @@ -263,4 +318,3 @@ void ScriptApiBase::objectrefGet(lua_State *L, u16 id) lua_remove(L, -2); // object_refs lua_remove(L, -2); // core } - diff --git a/src/script/cpp_api/s_base.h b/src/script/cpp_api/s_base.h index 4ea3677a9..d653b5bac 100644 --- a/src/script/cpp_api/s_base.h +++ b/src/script/cpp_api/s_base.h @@ -34,6 +34,25 @@ extern "C" { #include "common/c_internal.h" #define SCRIPTAPI_LOCK_DEBUG +#define SCRIPTAPI_DEBUG + +#define SCRIPT_MOD_NAME_FIELD "current_mod_name" +// MUST be an invalid mod name so that mods can't +// use that name to bypass security! +#define BUILTIN_MOD_NAME "*builtin*" + +#define PCALL_RES(RES) do { \ + int result_ = (RES); \ + if (result_ != 0) { \ + scriptError(result_, __FUNCTION__); \ + } \ +} while (0) + +#define runCallbacks(nargs, mode) \ + runCallbacksRaw((nargs), (mode), __FUNCTION__) + +#define setOriginFromTable(index) \ + setOriginFromTableRaw(index, __FUNCTION__) class Server; class Environment; @@ -42,17 +61,26 @@ class ServerActiveObject; class ScriptApiBase { public: - ScriptApiBase(); virtual ~ScriptApiBase(); - bool loadMod(const std::string &scriptpath, const std::string &modname); - bool loadScript(const std::string &scriptpath); + bool loadMod(const std::string &script_path, const std::string &mod_name, + std::string *error=NULL); + bool loadScript(const std::string &script_path, std::string *error=NULL); + + void runCallbacksRaw(int nargs, + RunCallbacksMode mode, const char *fxn); /* object */ void addObjectReference(ServerActiveObject *cobj); void removeObjectReference(ServerActiveObject *cobj); + Server* getServer() { return m_server; } + + std::string getOrigin() { return m_last_run_mod; } + void setOriginDirect(const char *origin); + void setOriginFromTableRaw(int index, const char *fxn); + protected: friend class LuaABM; friend class InvRef; @@ -66,10 +94,9 @@ protected: { return m_luastack; } void realityCheck(); - void scriptError(); + void scriptError(int result, const char *fxn); void stackDump(std::ostream &o); - Server* getServer() { return m_server; } void setServer(Server* server) { m_server = server; } Environment* getEnv() { return m_environment; } @@ -82,8 +109,10 @@ protected: void objectrefGet(lua_State *L, u16 id); JMutex m_luastackmutex; + std::string m_last_run_mod; // Stack index of Lua error handler int m_errorhandler; + bool m_secure; #ifdef SCRIPTAPI_LOCK_DEBUG bool m_locked; #endif diff --git a/src/script/cpp_api/s_entity.cpp b/src/script/cpp_api/s_entity.cpp index b52bde18a..0d159846a 100644 --- a/src/script/cpp_api/s_entity.cpp +++ b/src/script/cpp_api/s_entity.cpp @@ -91,9 +91,9 @@ void ScriptApiEntity::luaentity_Activate(u16 id, lua_pushvalue(L, object); // self lua_pushlstring(L, staticdata.c_str(), staticdata.size()); lua_pushinteger(L, dtime_s); - // Call with 3 arguments, 0 results - if (lua_pcall(L, 3, 0, m_errorhandler)) - scriptError(); + + setOriginFromTable(object); + PCALL_RES(lua_pcall(L, 3, 0, m_errorhandler)); } else { lua_pop(L, 1); } @@ -136,12 +136,12 @@ std::string ScriptApiEntity::luaentity_GetStaticdata(u16 id) lua_pop(L, 2); // Pop entity and get_staticdata return ""; } - luaL_checktype(L, -1, LUA_TFUNCTION); lua_pushvalue(L, object); // self - // Call with 1 arguments, 1 results - if (lua_pcall(L, 1, 1, m_errorhandler)) - scriptError(); + + setOriginFromTable(object); + PCALL_RES(lua_pcall(L, 1, 1, m_errorhandler)); + lua_remove(L, object); // Remove object size_t len = 0; @@ -209,9 +209,10 @@ void ScriptApiEntity::luaentity_Step(u16 id, float dtime) luaL_checktype(L, -1, LUA_TFUNCTION); lua_pushvalue(L, object); // self lua_pushnumber(L, dtime); // dtime - // Call with 2 arguments, 0 results - if (lua_pcall(L, 2, 0, m_errorhandler)) - scriptError(); + + setOriginFromTable(object); + PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler)); + lua_pop(L, 1); // Pop object } @@ -241,9 +242,10 @@ void ScriptApiEntity::luaentity_Punch(u16 id, lua_pushnumber(L, time_from_last_punch); push_tool_capabilities(L, *toolcap); push_v3f(L, dir); - // Call with 5 arguments, 0 results - if (lua_pcall(L, 5, 0, m_errorhandler)) - scriptError(); + + setOriginFromTable(object); + PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler)); + lua_pop(L, 1); // Pop object } @@ -268,9 +270,10 @@ void ScriptApiEntity::luaentity_Rightclick(u16 id, luaL_checktype(L, -1, LUA_TFUNCTION); lua_pushvalue(L, object); // self objectrefGetOrCreate(L, clicker); // Clicker reference - // Call with 2 arguments, 0 results - if (lua_pcall(L, 2, 0, m_errorhandler)) - scriptError(); + + setOriginFromTable(object); + PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler)); + lua_pop(L, 1); // Pop object } diff --git a/src/script/cpp_api/s_env.cpp b/src/script/cpp_api/s_env.cpp index c171bbf02..9c733773a 100644 --- a/src/script/cpp_api/s_env.cpp +++ b/src/script/cpp_api/s_env.cpp @@ -38,7 +38,7 @@ void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp, push_v3s16(L, minp); push_v3s16(L, maxp); lua_pushnumber(L, blockseed); - script_run_callbacks(L, 3, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(3, RUN_CALLBACKS_MODE_FIRST); } void ScriptApiEnv::environment_Step(float dtime) @@ -52,7 +52,7 @@ void ScriptApiEnv::environment_Step(float dtime) // Call callbacks lua_pushnumber(L, dtime); try { - script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); } catch (LuaError &e) { getServer()->setAsyncFatalError(e.what()); } @@ -73,7 +73,7 @@ void ScriptApiEnv::player_event(ServerActiveObject* player, std::string type) objectrefGetOrCreate(L, player); // player lua_pushstring(L,type.c_str()); // event type try { - script_run_callbacks(L, 2, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(2, RUN_CALLBACKS_MODE_FIRST); } catch (LuaError &e) { getServer()->setAsyncFatalError(e.what()); } diff --git a/src/script/cpp_api/s_internal.h b/src/script/cpp_api/s_internal.h index 10ee1a7de..9999a584a 100644 --- a/src/script/cpp_api/s_internal.h +++ b/src/script/cpp_api/s_internal.h @@ -61,3 +61,4 @@ bool* m_variable; StackUnroller stack_unroller(L); #endif /* S_INTERNAL_H_ */ + diff --git a/src/script/cpp_api/s_inventory.cpp b/src/script/cpp_api/s_inventory.cpp index 422faf150..019d1ccc0 100644 --- a/src/script/cpp_api/s_inventory.cpp +++ b/src/script/cpp_api/s_inventory.cpp @@ -48,8 +48,7 @@ int ScriptApiDetached::detached_inventory_AllowMove( lua_pushinteger(L, to_index + 1); // to_index lua_pushinteger(L, count); // count objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 7, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 7, 1, m_errorhandler)); if(!lua_isnumber(L, -1)) throw LuaError("allow_move should return a number. name=" + name); int ret = luaL_checkinteger(L, -1); @@ -77,8 +76,7 @@ int ScriptApiDetached::detached_inventory_AllowPut( lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 5, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler)); if (!lua_isnumber(L, -1)) throw LuaError("allow_put should return a number. name=" + name); int ret = luaL_checkinteger(L, -1); @@ -106,8 +104,7 @@ int ScriptApiDetached::detached_inventory_AllowTake( lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 5, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler)); if (!lua_isnumber(L, -1)) throw LuaError("allow_take should return a number. name=" + name); int ret = luaL_checkinteger(L, -1); @@ -139,8 +136,7 @@ void ScriptApiDetached::detached_inventory_OnMove( lua_pushinteger(L, to_index + 1); // to_index lua_pushinteger(L, count); // count objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 7, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 7, 0, m_errorhandler)); } // Report put items @@ -164,8 +160,7 @@ void ScriptApiDetached::detached_inventory_OnPut( lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 5, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler)); } // Report taken items @@ -189,8 +184,7 @@ void ScriptApiDetached::detached_inventory_OnTake( lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 5, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler)); } // Retrieves core.detached_inventories[name][callbackname] @@ -215,6 +209,9 @@ bool ScriptApiDetached::getDetachedInventoryCallback( lua_pop(L, 1); return false; } + + setOriginFromTable(-1); + lua_getfield(L, -1, callbackname); lua_remove(L, -2); // Should be a function or nil diff --git a/src/script/cpp_api/s_item.cpp b/src/script/cpp_api/s_item.cpp index e3a9ac7aa..4d4d416ec 100644 --- a/src/script/cpp_api/s_item.cpp +++ b/src/script/cpp_api/s_item.cpp @@ -42,8 +42,7 @@ bool ScriptApiItem::item_OnDrop(ItemStack &item, LuaItemStack::create(L, item); objectrefGetOrCreate(L, dropper); pushFloatPos(L, pos); - if (lua_pcall(L, 3, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 3, 1, m_errorhandler)); if (!lua_isnil(L, -1)) { try { item = read_item(L,-1, getServer()); @@ -68,8 +67,7 @@ bool ScriptApiItem::item_OnPlace(ItemStack &item, LuaItemStack::create(L, item); objectrefGetOrCreate(L, placer); pushPointedThing(pointed); - if (lua_pcall(L, 3, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 3, 1, m_errorhandler)); if (!lua_isnil(L, -1)) { try { item = read_item(L,-1, getServer()); @@ -94,8 +92,7 @@ bool ScriptApiItem::item_OnUse(ItemStack &item, LuaItemStack::create(L, item); objectrefGetOrCreate(L, user); pushPointedThing(pointed); - if (lua_pcall(L, 3, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 3, 1, m_errorhandler)); if(!lua_isnil(L, -1)) { try { item = read_item(L,-1, getServer()); @@ -116,7 +113,7 @@ bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user, lua_getfield(L, -1, "on_craft"); LuaItemStack::create(L, item); objectrefGetOrCreate(L, user); - + // Push inventory list std::vector<ItemStack> items; for (u32 i = 0; i < old_craft_grid->getSize(); i++) { @@ -125,8 +122,7 @@ bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user, push_items(L, items); InvRef::create(L, craft_inv); - if (lua_pcall(L, 4, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 4, 1, m_errorhandler)); if (!lua_isnil(L, -1)) { try { item = read_item(L,-1, getServer()); @@ -156,8 +152,7 @@ bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user, push_items(L, items); InvRef::create(L, craft_inv); - if (lua_pcall(L, 4, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 4, 1, m_errorhandler)); if (!lua_isnil(L, -1)) { try { item = read_item(L,-1, getServer()); @@ -198,6 +193,9 @@ bool ScriptApiItem::getItemCallback(const char *name, const char *callbackname) lua_remove(L, -2); luaL_checktype(L, -1, LUA_TTABLE); } + + setOriginFromTable(-1); + lua_getfield(L, -1, callbackname); lua_remove(L, -2); // Remove item def // Should be a function or nil diff --git a/src/script/cpp_api/s_mainmenu.cpp b/src/script/cpp_api/s_mainmenu.cpp index ef8cea6c9..17ceff082 100644 --- a/src/script/cpp_api/s_mainmenu.cpp +++ b/src/script/cpp_api/s_mainmenu.cpp @@ -21,15 +21,21 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_internal.h" #include "common/c_converter.h" -void ScriptApiMainMenu::setMainMenuErrorMessage(std::string errormessage) +void ScriptApiMainMenu::setMainMenuData(MainMenuDataForScript *data) { SCRIPTAPI_PRECHECKHEADER lua_getglobal(L, "gamedata"); int gamedata_idx = lua_gettop(L); lua_pushstring(L, "errormessage"); - lua_pushstring(L, errormessage.c_str()); + if (!data->errormessage.empty()) { + lua_pushstring(L, data->errormessage.c_str()); + } else { + lua_pushnil(L); + } lua_settable(L, gamedata_idx); + setboolfield(L, gamedata_idx, "reconnect_requested", + data->reconnect_requested); lua_pop(L, 1); } @@ -49,11 +55,10 @@ void ScriptApiMainMenu::handleMainMenuEvent(std::string text) // Call it lua_pushstring(L, text.c_str()); - if (lua_pcall(L, 1, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); } -void ScriptApiMainMenu::handleMainMenuButtons(std::map<std::string, std::string> fields) +void ScriptApiMainMenu::handleMainMenuButtons(const StringMap &fields) { SCRIPTAPI_PRECHECKHEADER @@ -69,8 +74,8 @@ void ScriptApiMainMenu::handleMainMenuButtons(std::map<std::string, std::string> // Convert fields to a Lua table lua_newtable(L); - std::map<std::string, std::string>::const_iterator it; - for (it = fields.begin(); it != fields.end(); it++){ + StringMap::const_iterator it; + for (it = fields.begin(); it != fields.end(); ++it) { const std::string &name = it->first; const std::string &value = it->second; lua_pushstring(L, name.c_str()); @@ -79,7 +84,6 @@ void ScriptApiMainMenu::handleMainMenuButtons(std::map<std::string, std::string> } // Call it - if (lua_pcall(L, 1, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); } diff --git a/src/script/cpp_api/s_mainmenu.h b/src/script/cpp_api/s_mainmenu.h index 53dcd37e9..8d5895817 100644 --- a/src/script/cpp_api/s_mainmenu.h +++ b/src/script/cpp_api/s_mainmenu.h @@ -21,17 +21,16 @@ with this program; if not, write to the Free Software Foundation, Inc., #define S_MAINMENU_H_ #include "cpp_api/s_base.h" -#include <map> +#include "util/string.h" +#include "../guiMainMenu.h" -class ScriptApiMainMenu - : virtual public ScriptApiBase -{ +class ScriptApiMainMenu : virtual public ScriptApiBase { public: /** - * set gamedata.errormessage to inform lua of an error - * @param errormessage the error message + * Hand over MainMenuDataForScript to lua to inform lua of the content + * @param data the data */ - void setMainMenuErrorMessage(std::string errormessage); + void setMainMenuData(MainMenuDataForScript *data); /** * process events received from formspec @@ -43,7 +42,7 @@ public: * process field data recieved from formspec * @param fields data in field format */ - void handleMainMenuButtons(std::map<std::string, std::string> fields); + void handleMainMenuButtons(const StringMap &fields); }; #endif /* S_MAINMENU_H_ */ diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp index e3d3fb58b..dac058b13 100644 --- a/src/script/cpp_api/s_node.cpp +++ b/src/script/cpp_api/s_node.cpp @@ -106,8 +106,7 @@ bool ScriptApiNode::node_on_punch(v3s16 p, MapNode node, pushnode(L, node, ndef); objectrefGetOrCreate(L, puncher); pushPointedThing(pointed); - if (lua_pcall(L, 4, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 4, 0, m_errorhandler)); return true; } @@ -126,8 +125,7 @@ bool ScriptApiNode::node_on_dig(v3s16 p, MapNode node, push_v3s16(L, p); pushnode(L, node, ndef); objectrefGetOrCreate(L, digger); - if (lua_pcall(L, 3, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 3, 0, m_errorhandler)); return true; } @@ -143,8 +141,7 @@ void ScriptApiNode::node_on_construct(v3s16 p, MapNode node) // Call function push_v3s16(L, p); - if (lua_pcall(L, 1, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); } void ScriptApiNode::node_on_destruct(v3s16 p, MapNode node) @@ -159,8 +156,7 @@ void ScriptApiNode::node_on_destruct(v3s16 p, MapNode node) // Call function push_v3s16(L, p); - if (lua_pcall(L, 1, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); } void ScriptApiNode::node_after_destruct(v3s16 p, MapNode node) @@ -176,8 +172,7 @@ void ScriptApiNode::node_after_destruct(v3s16 p, MapNode node) // Call function push_v3s16(L, p); pushnode(L, node, ndef); - if (lua_pcall(L, 2, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler)); } bool ScriptApiNode::node_on_timer(v3s16 p, MapNode node, f32 dtime) @@ -193,14 +188,13 @@ bool ScriptApiNode::node_on_timer(v3s16 p, MapNode node, f32 dtime) // Call function push_v3s16(L, p); lua_pushnumber(L,dtime); - if (lua_pcall(L, 2, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 2, 1, m_errorhandler)); return (bool) lua_isboolean(L, -1) && (bool) lua_toboolean(L, -1) == true; } void ScriptApiNode::node_on_receive_fields(v3s16 p, const std::string &formname, - const std::map<std::string, std::string> &fields, + const StringMap &fields, ServerActiveObject *sender) { SCRIPTAPI_PRECHECKHEADER @@ -220,8 +214,8 @@ void ScriptApiNode::node_on_receive_fields(v3s16 p, push_v3s16(L, p); // pos lua_pushstring(L, formname.c_str()); // formname lua_newtable(L); // fields - std::map<std::string, std::string>::const_iterator it; - for (it = fields.begin(); it != fields.end(); it++){ + StringMap::const_iterator it; + for (it = fields.begin(); it != fields.end(); it++) { const std::string &name = it->first; const std::string &value = it->second; lua_pushstring(L, name.c_str()); @@ -229,8 +223,7 @@ void ScriptApiNode::node_on_receive_fields(v3s16 p, lua_settable(L, -3); } objectrefGetOrCreate(L, sender); // player - if (lua_pcall(L, 4, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 4, 0, m_errorhandler)); } void ScriptApiNode::node_falling_update(v3s16 p) @@ -239,8 +232,7 @@ void ScriptApiNode::node_falling_update(v3s16 p) lua_getglobal(L, "nodeupdate"); push_v3s16(L, p); - if (lua_pcall(L, 1, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); } void ScriptApiNode::node_falling_update_single(v3s16 p) @@ -249,7 +241,5 @@ void ScriptApiNode::node_falling_update_single(v3s16 p) lua_getglobal(L, "nodeupdate_single"); push_v3s16(L, p); - if (lua_pcall(L, 1, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); } - diff --git a/src/script/cpp_api/s_node.h b/src/script/cpp_api/s_node.h index b3a6c83b5..fe1180cb3 100644 --- a/src/script/cpp_api/s_node.h +++ b/src/script/cpp_api/s_node.h @@ -20,11 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef S_NODE_H_ #define S_NODE_H_ -#include <map> - #include "irr_v3d.h" #include "cpp_api/s_base.h" #include "cpp_api/s_nodemeta.h" +#include "util/string.h" struct MapNode; class ServerActiveObject; @@ -47,7 +46,7 @@ public: bool node_on_timer(v3s16 p, MapNode node, f32 dtime); void node_on_receive_fields(v3s16 p, const std::string &formname, - const std::map<std::string, std::string> &fields, + const StringMap &fields, ServerActiveObject *sender); void node_falling_update(v3s16 p); void node_falling_update_single(v3s16 p); diff --git a/src/script/cpp_api/s_nodemeta.cpp b/src/script/cpp_api/s_nodemeta.cpp index 01eee337c..638750b0e 100644 --- a/src/script/cpp_api/s_nodemeta.cpp +++ b/src/script/cpp_api/s_nodemeta.cpp @@ -54,8 +54,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowMove(v3s16 p, lua_pushinteger(L, to_index + 1); // to_index lua_pushinteger(L, count); // count objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 7, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 7, 1, m_errorhandler)); if (!lua_isnumber(L, -1)) throw LuaError("allow_metadata_inventory_move should" " return a number, guilty node: " + nodename); @@ -89,8 +88,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowPut(v3s16 p, lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 5, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler)); if(!lua_isnumber(L, -1)) throw LuaError("allow_metadata_inventory_put should" " return a number, guilty node: " + nodename); @@ -124,8 +122,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowTake(v3s16 p, lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 5, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler)); if (!lua_isnumber(L, -1)) throw LuaError("allow_metadata_inventory_take should" " return a number, guilty node: " + nodename); @@ -162,8 +159,7 @@ void ScriptApiNodemeta::nodemeta_inventory_OnMove(v3s16 p, lua_pushinteger(L, to_index + 1); // to_index lua_pushinteger(L, count); // count objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 7, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 7, 0, m_errorhandler)); } // Report put items @@ -191,8 +187,7 @@ void ScriptApiNodemeta::nodemeta_inventory_OnPut(v3s16 p, lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 5, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler)); } // Report taken items @@ -220,13 +215,14 @@ void ScriptApiNodemeta::nodemeta_inventory_OnTake(v3s16 p, lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 5, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler)); } -ScriptApiNodemeta::ScriptApiNodemeta() { +ScriptApiNodemeta::ScriptApiNodemeta() +{ } -ScriptApiNodemeta::~ScriptApiNodemeta() { +ScriptApiNodemeta::~ScriptApiNodemeta() +{ } diff --git a/src/script/cpp_api/s_player.cpp b/src/script/cpp_api/s_player.cpp index 81bfd4505..ef3c31cfd 100644 --- a/src/script/cpp_api/s_player.cpp +++ b/src/script/cpp_api/s_player.cpp @@ -19,6 +19,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_player.h" #include "cpp_api/s_internal.h" +#include "common/c_converter.h" +#include "common/c_content.h" #include "util/string.h" void ScriptApiPlayer::on_newplayer(ServerActiveObject *player) @@ -30,7 +32,7 @@ void ScriptApiPlayer::on_newplayer(ServerActiveObject *player) lua_getfield(L, -1, "registered_on_newplayers"); // Call callbacks objectrefGetOrCreate(L, player); - script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); } void ScriptApiPlayer::on_dieplayer(ServerActiveObject *player) @@ -42,7 +44,47 @@ void ScriptApiPlayer::on_dieplayer(ServerActiveObject *player) lua_getfield(L, -1, "registered_on_dieplayers"); // Call callbacks objectrefGetOrCreate(L, player); - script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); +} + +bool ScriptApiPlayer::on_punchplayer(ServerActiveObject *player, + ServerActiveObject *hitter, + float time_from_last_punch, + const ToolCapabilities *toolcap, + v3f dir, + s16 damage) +{ + SCRIPTAPI_PRECHECKHEADER + // Get core.registered_on_punchplayers + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_punchplayers"); + // Call callbacks + objectrefGetOrCreate(L, player); + objectrefGetOrCreate(L, hitter); + lua_pushnumber(L, time_from_last_punch); + push_tool_capabilities(L, *toolcap); + push_v3f(L, dir); + lua_pushnumber(L, damage); + runCallbacks(6, RUN_CALLBACKS_MODE_OR); + return lua_toboolean(L, -1); +} + +s16 ScriptApiPlayer::on_player_hpchange(ServerActiveObject *player, + s16 hp_change) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get core.registered_on_player_hpchange + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_player_hpchange"); + lua_remove(L, -2); + + objectrefGetOrCreate(L, player); + lua_pushnumber(L, hp_change); + PCALL_RES(lua_pcall(L, 2, 1, m_errorhandler)); + hp_change = lua_tointeger(L, -1); + lua_pop(L, -1); + return hp_change; } bool ScriptApiPlayer::on_respawnplayer(ServerActiveObject *player) @@ -54,12 +96,15 @@ bool ScriptApiPlayer::on_respawnplayer(ServerActiveObject *player) lua_getfield(L, -1, "registered_on_respawnplayers"); // Call callbacks objectrefGetOrCreate(L, player); - script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_OR); + runCallbacks(1, RUN_CALLBACKS_MODE_OR); bool positioning_handled_by_some = lua_toboolean(L, -1); return positioning_handled_by_some; } -bool ScriptApiPlayer::on_prejoinplayer(std::string name, std::string ip, std::string &reason) +bool ScriptApiPlayer::on_prejoinplayer( + const std::string &name, + const std::string &ip, + std::string *reason) { SCRIPTAPI_PRECHECKHEADER @@ -68,9 +113,9 @@ bool ScriptApiPlayer::on_prejoinplayer(std::string name, std::string ip, std::st lua_getfield(L, -1, "registered_on_prejoinplayers"); lua_pushstring(L, name.c_str()); lua_pushstring(L, ip.c_str()); - script_run_callbacks(L, 2, RUN_CALLBACKS_MODE_OR); + runCallbacks(2, RUN_CALLBACKS_MODE_OR); if (lua_isstring(L, -1)) { - reason.assign(lua_tostring(L, -1)); + reason->assign(lua_tostring(L, -1)); return true; } return false; @@ -85,7 +130,7 @@ void ScriptApiPlayer::on_joinplayer(ServerActiveObject *player) lua_getfield(L, -1, "registered_on_joinplayers"); // Call callbacks objectrefGetOrCreate(L, player); - script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); } void ScriptApiPlayer::on_leaveplayer(ServerActiveObject *player) @@ -97,7 +142,7 @@ void ScriptApiPlayer::on_leaveplayer(ServerActiveObject *player) lua_getfield(L, -1, "registered_on_leaveplayers"); // Call callbacks objectrefGetOrCreate(L, player); - script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); } void ScriptApiPlayer::on_cheat(ServerActiveObject *player, @@ -113,12 +158,12 @@ void ScriptApiPlayer::on_cheat(ServerActiveObject *player, lua_newtable(L); lua_pushlstring(L, cheat_type.c_str(), cheat_type.size()); lua_setfield(L, -2, "type"); - script_run_callbacks(L, 2, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(2, RUN_CALLBACKS_MODE_FIRST); } void ScriptApiPlayer::on_playerReceiveFields(ServerActiveObject *player, const std::string &formname, - const std::map<std::string, std::string> &fields) + const StringMap &fields) { SCRIPTAPI_PRECHECKHEADER @@ -132,17 +177,19 @@ void ScriptApiPlayer::on_playerReceiveFields(ServerActiveObject *player, lua_pushstring(L, formname.c_str()); // param 3 lua_newtable(L); - for(std::map<std::string, std::string>::const_iterator - i = fields.begin(); i != fields.end(); i++){ - const std::string &name = i->first; - const std::string &value = i->second; + StringMap::const_iterator it; + for (it = fields.begin(); it != fields.end(); ++it) { + const std::string &name = it->first; + const std::string &value = it->second; lua_pushstring(L, name.c_str()); lua_pushlstring(L, value.c_str(), value.size()); lua_settable(L, -3); } - script_run_callbacks(L, 3, RUN_CALLBACKS_MODE_OR_SC); + runCallbacks(3, RUN_CALLBACKS_MODE_OR_SC); } -ScriptApiPlayer::~ScriptApiPlayer() { + +ScriptApiPlayer::~ScriptApiPlayer() +{ } diff --git a/src/script/cpp_api/s_player.h b/src/script/cpp_api/s_player.h index c77d397c4..2e4dc2222 100644 --- a/src/script/cpp_api/s_player.h +++ b/src/script/cpp_api/s_player.h @@ -20,10 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef S_PLAYER_H_ #define S_PLAYER_H_ -#include <map> - #include "cpp_api/s_base.h" +#include "irr_v3d.h" +#include "util/string.h" +struct ToolCapabilities; class ScriptApiPlayer : virtual public ScriptApiBase @@ -34,14 +35,17 @@ public: void on_newplayer(ServerActiveObject *player); void on_dieplayer(ServerActiveObject *player); bool on_respawnplayer(ServerActiveObject *player); - bool on_prejoinplayer(std::string name, std::string ip, std::string &reason); + bool on_prejoinplayer(const std::string &name, const std::string &ip, + std::string *reason); void on_joinplayer(ServerActiveObject *player); void on_leaveplayer(ServerActiveObject *player); void on_cheat(ServerActiveObject *player, const std::string &cheat_type); - + bool on_punchplayer(ServerActiveObject *player, + ServerActiveObject *hitter, float time_from_last_punch, + const ToolCapabilities *toolcap, v3f dir, s16 damage); + s16 on_player_hpchange(ServerActiveObject *player, s16 hp_change); void on_playerReceiveFields(ServerActiveObject *player, - const std::string &formname, - const std::map<std::string, std::string> &fields); + const std::string &formname, const StringMap &fields); }; diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp new file mode 100644 index 000000000..6a6d40307 --- /dev/null +++ b/src/script/cpp_api/s_security.cpp @@ -0,0 +1,604 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "cpp_api/s_security.h" + +#include "filesys.h" +#include "porting.h" +#include "server.h" +#include "settings.h" + +#include <cerrno> +#include <string> +#include <iostream> + + +#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) +{ + 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]); + } +} + +// Pushes the original version of a library function on the stack, from the old version +static inline void push_original(lua_State *L, const char *lib, const char *func) +{ + lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup"); + lua_getfield(L, -1, lib); + lua_remove(L, -2); // Remove globals_backup + lua_getfield(L, -1, func); + 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", + }; + static const char *io_whitelist[] = { + "close", + "flush", + "read", + "type", + "write", + }; + static const char *os_whitelist[] = { + "clock", + "date", + "difftime", + "exit", + "getenv", + "setlocale", + "time", + "tmpname", + }; + static const char *debug_whitelist[] = { + "gethook", + "traceback", + "getinfo", + "getmetatable", + "setupvalue", + "setmetatable", + "upvalueid", + "upvaluejoin", + "sethook", + "debug", + "getupvalue", + "setlocal", + }; + static const char *package_whitelist[] = { + "config", + "cpath", + "path", + "searchpath", + }; + static const char *jit_whitelist[] = { + "arch", + "flush", + "off", + "on", + "opt", + "os", + "status", + "version", + "version_num", + }; + + m_secure = true; + + lua_State *L = getStack(); + + // Backup globals to the registry + lua_getglobal(L, "_G"); + lua_setfield(L, LUA_REGISTRYINDEX, "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_getfield(L, LUA_REGISTRYINDEX, "globals_backup"); + int old_globals = lua_gettop(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, load); + SECURE_API(g, loadfile); + 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)); + + // And replace unsafe ones + SECURE_API(os, remove); + SECURE_API(os, rename); + + 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 + + + // 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 + + + // 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 + + lua_pop(L, 1); // Pop globals_backup +} + + +bool ScriptApiSecurity::isSecure(lua_State *L) +{ + lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup"); + bool secure = !lua_isnil(L, -1); + lua_pop(L, 1); + return secure; +} + + +#define CHECK_FILE_ERR(ret, fp) \ + if (ret) { \ + if (fp) std::fclose(fp); \ + lua_pushfstring(L, "%s: %s", path, strerror(errno)); \ + return false; \ + } + + +bool ScriptApiSecurity::safeLoadFile(lua_State *L, const char *path) +{ + FILE *fp; + char *chunk_name; + if (path == NULL) { + fp = stdin; + chunk_name = const_cast<char *>("=stdin"); + } else { + fp = fopen(path, "rb"); + if (!fp) { + lua_pushfstring(L, "%s: %s", path, strerror(errno)); + return false; + } + chunk_name = new char[strlen(path) + 2]; + chunk_name[0] = '@'; + chunk_name[1] = '\0'; + strcat(chunk_name, path); + } + + size_t start = 0; + int c = std::getc(fp); + if (c == '#') { + // Skip the first line + while ((c = std::getc(fp)) != EOF && c != '\n'); + if (c == '\n') c = std::getc(fp); + start = std::ftell(fp); + } + + if (c == LUA_SIGNATURE[0]) { + lua_pushliteral(L, "Bytecode prohibited when mod security is enabled."); + return false; + } + + // Read the file + int ret = std::fseek(fp, 0, SEEK_END); + CHECK_FILE_ERR(ret, fp); + if (ret) { + std::fclose(fp); + lua_pushfstring(L, "%s: %s", path, strerror(errno)); + return false; + } + size_t size = std::ftell(fp) - start; + char *code = new char[size]; + ret = std::fseek(fp, start, SEEK_SET); + CHECK_FILE_ERR(ret, fp); + if (ret) { + std::fclose(fp); + lua_pushfstring(L, "%s: %s", path, strerror(errno)); + return false; + } + size_t num_read = std::fread(code, 1, size, fp); + if (path) { + std::fclose(fp); + } + if (num_read != size) { + lua_pushliteral(L, "Error reading file to load."); + return false; + } + + if (luaL_loadbuffer(L, code, size, chunk_name)) { + return false; + } + + if (path) { + delete [] chunk_name; + } + return true; +} + + +bool ScriptApiSecurity::checkPath(lua_State *L, const char *path) +{ + std::string str; // Transient + + std::string norel_path = fs::RemoveRelativePathComponents(path); + std::string abs_path = fs::AbsolutePath(norel_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 we couldn't find the absolute path (path doesn't exist) then + // try removing the last components until it works (to allow + // non-existent files/folders for mkdir). + std::string cur_path = norel_path; + std::string removed; + while (abs_path.empty() && !cur_path.empty()) { + std::string tmp_rmed; + cur_path = fs::RemoveLastPathComponent(cur_path, &tmp_rmed); + removed = tmp_rmed + (removed.empty() ? "" : DIR_DELIM + removed); + abs_path = fs::AbsolutePath(cur_path); + } + if (abs_path.empty()) return false; + // Add the removed parts back so that you can't, eg, create a + // directory in worldmods if worldmods doesn't exist. + if (!removed.empty()) abs_path += DIR_DELIM + removed; + + // Get server from registry + lua_getfield(L, LUA_REGISTRYINDEX, "scriptapi"); + ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1); + lua_pop(L, 1); + const Server *server = script->getServer(); + + if (!server) return false; + + // Get mod name + lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD); + if (lua_isstring(L, -1)) { + std::string mod_name = lua_tostring(L, -1); + + // Builtin can access anything + if (mod_name == BUILTIN_MOD_NAME) { + return true; + } + + // Allow paths in mod path + const ModSpec *mod = server->getModSpec(mod_name); + if (mod) { + str = fs::AbsolutePath(mod->path); + if (!str.empty() && fs::PathStartsWith(abs_path, str)) { + return true; + } + } + } + lua_pop(L, 1); // Pop mod name + + str = fs::AbsolutePath(server->getWorldPath()); + if (str.empty()) return false; + // Don't allow access to world mods. We add to the absolute path + // of the world instead of getting the absolute paths directly + // because that won't work if they don't exist. + if (fs::PathStartsWith(abs_path, str + DIR_DELIM + "worldmods") || + fs::PathStartsWith(abs_path, str + DIR_DELIM + "game")) { + return false; + } + // Allow all other paths in world path + if (fs::PathStartsWith(abs_path, str)) { + return true; + } + + // Default to disallowing + return false; +} + + +int ScriptApiSecurity::sl_g_dofile(lua_State *L) +{ + int nret = sl_g_loadfile(L); + if (nret != 1) { + lua_error(L); + // code after this function isn't executed + } + int top_precall = lua_gettop(L); + lua_call(L, 0, LUA_MULTRET); + // Return number of arguments returned by the function, + // adjusting for the function being poped. + return lua_gettop(L) - (top_precall - 1); +} + + +int ScriptApiSecurity::sl_g_load(lua_State *L) +{ + size_t len; + const char *buf; + std::string code; + const char *chunk_name = "=(load)"; + + luaL_checktype(L, 1, LUA_TFUNCTION); + if (!lua_isnone(L, 2)) { + luaL_checktype(L, 2, LUA_TSTRING); + chunk_name = lua_tostring(L, 2); + } + + while (true) { + lua_pushvalue(L, 1); + lua_call(L, 0, 1); + int t = lua_type(L, -1); + if (t == LUA_TNIL) { + break; + } else if (t != LUA_TSTRING) { + lua_pushnil(L); + lua_pushliteral(L, "Loader didn't return a string"); + return 2; + } + buf = lua_tolstring(L, -1, &len); + code += std::string(buf, len); + lua_pop(L, 1); // Pop return value + } + if (code[0] == LUA_SIGNATURE[0]) { + lua_pushnil(L); + lua_pushliteral(L, "Bytecode prohibited when mod security is enabled."); + return 2; + } + if (luaL_loadbuffer(L, code.data(), code.size(), chunk_name)) { + lua_pushnil(L); + lua_insert(L, lua_gettop(L) - 1); + return 2; + } + return 1; +} + + +int ScriptApiSecurity::sl_g_loadfile(lua_State *L) +{ + const char *path = NULL; + + if (lua_isstring(L, 1)) { + path = lua_tostring(L, 1); + CHECK_SECURE_PATH(L, path); + } + + if (!safeLoadFile(L, path)) { + lua_pushnil(L); + lua_insert(L, -2); + return 2; + } + + return 1; +} + + +int ScriptApiSecurity::sl_g_loadstring(lua_State *L) +{ + const char *chunk_name = "=(load)"; + + luaL_checktype(L, 1, LUA_TSTRING); + if (!lua_isnone(L, 2)) { + luaL_checktype(L, 2, LUA_TSTRING); + chunk_name = lua_tostring(L, 2); + } + + size_t size; + const char *code = lua_tolstring(L, 1, &size); + + if (size > 0 && code[0] == LUA_SIGNATURE[0]) { + lua_pushnil(L); + lua_pushliteral(L, "Bytecode prohibited when mod security is enabled."); + return 2; + } + if (luaL_loadbuffer(L, code, size, chunk_name)) { + lua_pushnil(L); + lua_insert(L, lua_gettop(L) - 1); + return 2; + } + 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) +{ + luaL_checktype(L, 1, LUA_TSTRING); + const char *path = lua_tostring(L, 1); + CHECK_SECURE_PATH(L, path); + + push_original(L, "io", "open"); + lua_pushvalue(L, 1); + lua_pushvalue(L, 2); + lua_call(L, 2, 2); + return 2; +} + + +int ScriptApiSecurity::sl_io_input(lua_State *L) +{ + if (lua_isstring(L, 1)) { + const char *path = lua_tostring(L, 1); + CHECK_SECURE_PATH(L, path); + } + + push_original(L, "io", "input"); + lua_pushvalue(L, 1); + lua_call(L, 1, 1); + return 1; +} + + +int ScriptApiSecurity::sl_io_output(lua_State *L) +{ + if (lua_isstring(L, 1)) { + const char *path = lua_tostring(L, 1); + CHECK_SECURE_PATH(L, path); + } + + push_original(L, "io", "output"); + lua_pushvalue(L, 1); + lua_call(L, 1, 1); + return 1; +} + + +int ScriptApiSecurity::sl_io_lines(lua_State *L) +{ + if (lua_isstring(L, 1)) { + const char *path = lua_tostring(L, 1); + CHECK_SECURE_PATH(L, path); + } + + push_original(L, "io", "lines"); + lua_pushvalue(L, 1); + int top_precall = lua_gettop(L); + lua_call(L, 1, LUA_MULTRET); + // Return number of arguments returned by the function, + // adjusting for the function being poped. + return lua_gettop(L) - (top_precall - 1); +} + + +int ScriptApiSecurity::sl_os_rename(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TSTRING); + const char *path1 = lua_tostring(L, 1); + CHECK_SECURE_PATH(L, path1); + + luaL_checktype(L, 2, LUA_TSTRING); + const char *path2 = lua_tostring(L, 2); + CHECK_SECURE_PATH(L, path2); + + push_original(L, "os", "rename"); + lua_pushvalue(L, 1); + lua_pushvalue(L, 2); + lua_call(L, 2, 2); + return 2; +} + + +int ScriptApiSecurity::sl_os_remove(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TSTRING); + const char *path = lua_tostring(L, 1); + CHECK_SECURE_PATH(L, path); + + push_original(L, "os", "remove"); + lua_pushvalue(L, 1); + lua_call(L, 1, 2); + return 2; +} + diff --git a/src/script/cpp_api/s_security.h b/src/script/cpp_api/s_security.h new file mode 100644 index 000000000..4a4389cf5 --- /dev/null +++ b/src/script/cpp_api/s_security.h @@ -0,0 +1,70 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef S_SECURITY_H +#define S_SECURITY_H + +#include "cpp_api/s_base.h" + + +#define CHECK_SECURE_PATH(L, path) \ + if (!ScriptApiSecurity::checkPath(L, path)) { \ + lua_pushstring(L, (std::string("Attempt to access external file ") + \ + path + " with mod security on.").c_str()); \ + lua_error(L); \ + } +#define CHECK_SECURE_PATH_OPTIONAL(L, path) \ + if (ScriptApiSecurity::isSecure(L)) { \ + CHECK_SECURE_PATH(L, path); \ + } + + +class ScriptApiSecurity : virtual public ScriptApiBase +{ +public: + // Sets up security on the ScriptApi's Lua state + void initializeSecurity(); + // 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). + static bool safeLoadFile(lua_State *L, const char *path); + // Checks if mods are allowed to read and write to the path + static bool checkPath(lua_State *L, const char *path); + +private: + // Syntax: "sl_" <Library name or 'g' (global)> '_' <Function name> + // (sl stands for Secure Lua) + + static int sl_g_dofile(lua_State *L); + static int sl_g_load(lua_State *L); + static int sl_g_loadfile(lua_State *L); + static int sl_g_loadstring(lua_State *L); + static int sl_g_require(lua_State *L); + + static int sl_io_open(lua_State *L); + static int sl_io_input(lua_State *L); + static int sl_io_output(lua_State *L); + static int sl_io_lines(lua_State *L); + + static int sl_os_rename(lua_State *L); + static int sl_os_remove(lua_State *L); +}; + +#endif + diff --git a/src/script/cpp_api/s_server.cpp b/src/script/cpp_api/s_server.cpp index 21fe164aa..ec2f9c0af 100644 --- a/src/script/cpp_api/s_server.cpp +++ b/src/script/cpp_api/s_server.cpp @@ -32,8 +32,7 @@ bool ScriptApiServer::getAuth(const std::string &playername, if (lua_type(L, -1) != LUA_TFUNCTION) throw LuaError("Authentication handler missing get_auth"); lua_pushstring(L, playername.c_str()); - if (lua_pcall(L, 1, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 1, 1, m_errorhandler)); lua_remove(L, -2); // Remove auth handler // nil = login not allowed @@ -68,6 +67,9 @@ void ScriptApiServer::getAuthHandler() lua_pop(L, 1); lua_getfield(L, -1, "builtin_auth_handler"); } + + setOriginFromTable(-1); + lua_remove(L, -2); // Remove core if (lua_type(L, -1) != LUA_TTABLE) throw LuaError("Authentication handler table not valid"); @@ -104,8 +106,7 @@ void ScriptApiServer::createAuth(const std::string &playername, throw LuaError("Authentication handler missing create_auth"); lua_pushstring(L, playername.c_str()); lua_pushstring(L, password.c_str()); - if (lua_pcall(L, 2, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler)); } bool ScriptApiServer::setPassword(const std::string &playername, @@ -120,8 +121,7 @@ bool ScriptApiServer::setPassword(const std::string &playername, throw LuaError("Authentication handler missing set_password"); lua_pushstring(L, playername.c_str()); lua_pushstring(L, password.c_str()); - if (lua_pcall(L, 2, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 2, 1, m_errorhandler)); return lua_toboolean(L, -1); } @@ -136,7 +136,7 @@ bool ScriptApiServer::on_chat_message(const std::string &name, // Call callbacks lua_pushstring(L, name.c_str()); lua_pushstring(L, message.c_str()); - script_run_callbacks(L, 2, RUN_CALLBACKS_MODE_OR_SC); + runCallbacks(2, RUN_CALLBACKS_MODE_OR_SC); bool ate = lua_toboolean(L, -1); return ate; } @@ -149,6 +149,6 @@ void ScriptApiServer::on_shutdown() lua_getglobal(L, "core"); lua_getfield(L, -1, "registered_on_shutdown"); // Call callbacks - script_run_callbacks(L, 0, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(0, RUN_CALLBACKS_MODE_FIRST); } |