Deprecate writing to mod directories (#14486)

This commit is contained in:
rubenwardy 2024-03-27 18:32:05 +00:00 committed by GitHub
parent 6a7a613741
commit b487341c32
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 51 additions and 20 deletions

@ -505,6 +505,28 @@ bool ScriptApiSecurity::safeLoadFile(lua_State *L, const char *path, const char
} }
bool checkModNameWhitelisted(const std::string &mod_name, const std::string &setting)
{
assert(str_starts_with(setting, "secure."));
if (mod_name.empty())
return false;
std::string value = g_settings->get(setting);
value.erase(std::remove(value.begin(), value.end(), ' '), value.end());
auto mod_list = str_split(value, ',');
return CONTAINS(mod_list, mod_name);
}
bool ScriptApiSecurity::checkWhitelisted(lua_State *L, const std::string &setting)
{
std::string mod_name = ScriptApiBase::getCurrentModName(L);
return checkModNameWhitelisted(mod_name, setting);
}
bool ScriptApiSecurity::checkPath(lua_State *L, const char *path, bool ScriptApiSecurity::checkPath(lua_State *L, const char *path,
bool write_required, bool *write_allowed) bool write_required, bool *write_allowed)
{ {
@ -555,7 +577,6 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path,
return false; return false;
// Get mod name // Get mod name
// FIXME: insecure = problem here?
std::string mod_name = ScriptApiBase::getCurrentModNameInsecure(L); std::string mod_name = ScriptApiBase::getCurrentModNameInsecure(L);
if (!mod_name.empty()) { if (!mod_name.empty()) {
// Builtin can access anything // Builtin can access anything
@ -571,7 +592,32 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path,
if (mod) { if (mod) {
str = fs::AbsolutePath(mod->path); str = fs::AbsolutePath(mod->path);
if (!str.empty() && fs::PathStartsWith(abs_path, str)) { if (!str.empty() && fs::PathStartsWith(abs_path, str)) {
if (write_allowed) *write_allowed = true; // `mod_name` cannot be trusted here, so we catch the scenarios where this becomes a problem:
bool is_trusted = checkModNameWhitelisted(mod_name, "secure.trusted_mods") ||
checkModNameWhitelisted(mod_name, "secure.http_mods");
std::string filename = lowercase(fs::GetFilenameFromPath(abs_path.c_str()));
// By writing to any of these a malicious mod could turn itself into
// an existing trusted mod by renaming or becoming a modpack.
bool is_dangerous_file = filename == "mod.conf" ||
filename == "modpack.conf" ||
filename == "modpack.txt";
if (write_required) {
if (is_trusted) {
throw LuaError(
"Unable to write to a trusted or http mod's directory. "
"For data storage consider minetest.get_mod_data_path() or minetest.get_worldpath() instead.");
} else if (is_dangerous_file) {
throw LuaError(
"Unable to write to special file for security reasons");
} else {
const char *message =
"Writing to mod directories is deprecated, as any changes "
"will be overwritten when updating content. "
"For data storage consider minetest.get_mod_data_path() or minetest.get_worldpath() instead.";
log_deprecated(L, message, 1);
}
}
if (write_allowed) *write_allowed = !is_trusted && !is_dangerous_file;
return true; return true;
} }
} }
@ -630,21 +676,6 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path,
return false; return false;
} }
bool ScriptApiSecurity::checkWhitelisted(lua_State *L, const std::string &setting)
{
assert(str_starts_with(setting, "secure."));
std::string mod_name = ScriptApiBase::getCurrentModName(L);
if (mod_name.empty())
return false;
std::string value = g_settings->get(setting);
value.erase(std::remove(value.begin(), value.end(), ' '), value.end());
auto mod_list = str_split(value, ',');
return CONTAINS(mod_list, mod_name);
}
int ScriptApiSecurity::sl_g_dofile(lua_State *L) int ScriptApiSecurity::sl_g_dofile(lua_State *L)
{ {

@ -49,12 +49,12 @@ public:
static bool safeLoadString(lua_State *L, const std::string &code, const char *chunk_name); static bool safeLoadString(lua_State *L, const std::string &code, const char *chunk_name);
// Loads a file as Lua code safely (doesn't allow bytecode). // Loads a file as Lua code safely (doesn't allow bytecode).
static bool safeLoadFile(lua_State *L, const char *path, const char *display_name = NULL); static bool safeLoadFile(lua_State *L, const char *path, const char *display_name = NULL);
// Checks if mods are allowed to read (and optionally write) to the path
static bool checkPath(lua_State *L, const char *path, bool write_required,
bool *write_allowed=NULL);
// Check if mod is whitelisted in the given setting // Check if mod is whitelisted in the given setting
// This additionally checks that the mod's main file scope is executing. // This additionally checks that the mod's main file scope is executing.
static bool checkWhitelisted(lua_State *L, const std::string &setting); static bool checkWhitelisted(lua_State *L, const std::string &setting);
// Checks if mods are allowed to read (and optionally write) to the path
static bool checkPath(lua_State *L, const char *path, bool write_required,
bool *write_allowed=NULL);
private: private:
int getThread(lua_State *L); int getThread(lua_State *L);