Exemplo n.º 1
0
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;
}
Exemplo n.º 2
0
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 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 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 = path;
	std::string removed;
	while (abs_path.empty() && !cur_path.empty()) {
		std::string component;
		cur_path = fs::RemoveLastPathComponent(cur_path, &component);
		if (component == "..") {
			// 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.
			return false;
		}
		removed = component + (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_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_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_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
	if (lua_isstring(L, -1)) {
		std::string mod_name = lua_tostring(L, -1);

		// Builtin can access anything
		if (mod_name == BUILTIN_MOD_NAME) {
			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
		if (write_required || write_allowed != NULL) {
			const ModSpec *mod = server->getModSpec(mod_name);
			if (mod) {
				str = fs::AbsolutePath(mod->path);
				if (!str.empty() && fs::PathStartsWith(abs_path, str)) {
					if (write_allowed) *write_allowed = true;
					return true;
				}
			}
		}
	}
	lua_pop(L, 1);  // Pop mod name

	// Allow read-only access to all mod directories
	if (!write_required) {
		const std::vector<ModSpec> mods = server->getMods();
		for (size_t i = 0; i < mods.size(); ++i) {
			str = fs::AbsolutePath(mods[i].path);
			if (!str.empty() && fs::PathStartsWith(abs_path, str)) {
				return true;
			}
		}
	}

	str = fs::AbsolutePath(server->getWorldPath());
	if (!str.empty()) {
		// Don't allow access to other paths in the world mod/game path.
		// These have to be blocked so you can't override a trusted mod
		// by creating a mod with the same name in a world mod directory.
		// 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)) {
			if (write_allowed) *write_allowed = true;
			return true;
		}
	}

	// Default to disallowing
	return false;
}