aboutsummaryrefslogtreecommitdiff
path: root/src/script/cpp_api/s_security.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/script/cpp_api/s_security.cpp')
-rw-r--r--src/script/cpp_api/s_security.cpp360
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;
}
+