From: Shravan Rangarajuvenkata (shrarang) Date: Wed, 2 Dec 2020 17:43:31 +0000 (+0000) Subject: Merge pull request #2608 in SNORT/snort3 from ~SHRARANG/snort3:lua_sandbox to master X-Git-Tag: 3.0.3-6~23 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c5376325841289ca4af8e313d96732dbc656fc10;p=thirdparty%2Fsnort3.git Merge pull request #2608 in SNORT/snort3 from ~SHRARANG/snort3:lua_sandbox to master Squashed commit of the following: commit bd0c2a888b69a9791bb2b8dd196c8a6fdd53ca1c Author: Shravan Rangaraju Date: Fri Jul 31 12:05:26 2020 -0400 shell: support for loading configuration in lua sandbox Load snort configuration in a Lua sandbox. Sandbox configuration file can be specified with the command line option "--lua-sandbox". Snort expects this file to contain a Lua table named sandbox_env. sandbox_env should specify a list of allowed Lua functions. This table is used as the sandbox environment. Snort loads the following in the sandbox: - top-level configuration file specified by command line option "-c" - subpolicy files - all of the included files in above files and also files included in the included files - configuration overrides specified with command line option "--lua" If any of the above use Lua functionality not allowed in sandbox_env, snort will exit with a fatal error. --- diff --git a/src/host_tracker/test/host_cache_module_test.cc b/src/host_tracker/test/host_cache_module_test.cc index 567452cdc..c730144b3 100644 --- a/src/host_tracker/test/host_cache_module_test.cc +++ b/src/host_tracker/test/host_cache_module_test.cc @@ -73,7 +73,7 @@ extern "C" typedef ptrdiff_t lua_Integer; const char* luaL_optlstring(lua_State*, int, const char*, size_t*) { return nullptr; } -int luaL_optinteger(lua_State*, int, lua_Integer) { return 0; } +lua_Integer luaL_optinteger(lua_State*, int, lua_Integer) { return 0; } } void show_stats(PegCount*, const PegInfo*, unsigned, const char*) { } diff --git a/src/managers/lua_wrap.sh b/src/lua_wrap.sh similarity index 100% rename from src/managers/lua_wrap.sh rename to src/lua_wrap.sh diff --git a/src/main/CMakeLists.txt b/src/main/CMakeLists.txt index 30b0f6add..95aa8a465 100644 --- a/src/main/CMakeLists.txt +++ b/src/main/CMakeLists.txt @@ -12,6 +12,11 @@ set (INCLUDES thread_config.h ) +set (LOCAL_INCLUDES + ${CMAKE_CURRENT_BINARY_DIR}/lua_bootstrap.h + ${CMAKE_CURRENT_BINARY_DIR}/lua_finalize.h +) + if ( ENABLE_SHELL ) set ( SHELL_SOURCES control.cc control.h control_mgmt.cc control_mgmt.h ac_shell_cmd.h ac_shell_cmd.cc) endif ( ENABLE_SHELL ) @@ -44,9 +49,24 @@ add_library (main OBJECT thread_config.h thread_config.cc ${INCLUDES} + ${LOCAL_INCLUDES} ${SHELL_SOURCES} ) +add_custom_command ( + OUTPUT lua_bootstrap.h + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/../lua_wrap.sh ${CMAKE_CURRENT_SOURCE_DIR} bootstrap > lua_bootstrap.h + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bootstrap.lua +) + +add_custom_command ( + OUTPUT lua_finalize.h + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/../lua_wrap.sh ${CMAKE_CURRENT_SOURCE_DIR} finalize > lua_finalize.h + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/finalize.lua +) + +include_directories (${CMAKE_CURRENT_BINARY_DIR}) + install (FILES ${INCLUDES} DESTINATION "${INCLUDE_INSTALL_PATH}/main" ) diff --git a/src/managers/bootstrap.lua b/src/main/bootstrap.lua similarity index 73% rename from src/managers/bootstrap.lua rename to src/main/bootstrap.lua index 17b20ea0e..e1d5af714 100644 --- a/src/managers/bootstrap.lua +++ b/src/main/bootstrap.lua @@ -92,14 +92,61 @@ function include(file) local cname = ffi.C.push_include_path(file) local fname = ffi.string(cname); path_push(fname) - dofile(fname) + + if ( sandbox_env ) then + local file_in = assert(io.open(fname, "r")) + local file_content = file_in:read("*all") + file_in:close() + + if ( file_content:byte(1) == 27 ) then + error("bytecode is not allowed") + end + + local fn = assert(loadstring(file_content)) + setfenv(fn, sandbox_env) + fn() + + if ( sandbox_env.ips ) then + ips = sandbox_env.ips + end + else + dofile(fname) + end + local iname = path_top() if ( (ips ~= nil) and (ips.includer == nil) and (iname ~= nil) ) then ips.includer = iname end + path_pop() ffi.C.pop_include_path() end +function sandbox_include(file) + assert(sandbox_env) + include (file) +end + +function create_sandbox_env() + local export_to_sandbox = + { + include = sandbox_include, + snort_whitelist_add_prefix = snort_whitelist_add_prefix, + snort_whitelist_append = snort_whitelist_append, + SNORT_VERSION = SNORT_VERSION, + SNORT_MAJOR_VERSION = SNORT_MAJOR_VERSION, + SNORT_MINOR_VERSION = SNORT_MINOR_VERSION, + SNORT_PATCH_VERSION = SNORT_PATCH_VERSION, + tweaks = tweaks, + } + + for k, v in pairs(export_to_sandbox) do + if ( sandbox_env[k] ) then + error(k .. " cannot be redefined") + end + sandbox_env[k] = v + end +end + initialize_whitelist(_G) initialize_whitelist = nil diff --git a/src/managers/finalize.lua b/src/main/finalize.lua similarity index 88% rename from src/managers/finalize.lua rename to src/main/finalize.lua index 47d3e0266..83d2df7a8 100644 --- a/src/managers/finalize.lua +++ b/src/main/finalize.lua @@ -77,27 +77,31 @@ function snort_set(fqn, key, val) end end -function load_aliases() - for i,v in ipairs(binder) do +function load_aliases(env) + for i,v in ipairs(env.binder) do if ( v.use and type(v.use) == "table" ) then if ( v.use.name and v.use.type ) then ffi.C.set_alias(v.use.name, v.use.type) - local tab = _G[v.use.name] + local tab = env[v.use.name] if ( tab ) then snort_whitelist_append(v.use.name) - snort_set(nil, v.use.name, _G[v.use.name]) + snort_set(nil, v.use.name, env[v.use.name]) end end end end end -function snort_config(tab) - if ( binder and type(binder) == 'table' ) then - load_aliases() +function snort_config(env) + if ( env.binder and type(env.binder) == 'table' ) then + load_aliases(env) end - snort_traverse(tab) + snort_traverse(env) end -snort_config(_G) +if (sandbox_env) then + snort_config(sandbox_env) +else + snort_config(_G) +end diff --git a/src/main/shell.cc b/src/main/shell.cc index da8386ba8..c66cab452 100644 --- a/src/main/shell.cc +++ b/src/main/shell.cc @@ -26,6 +26,7 @@ #include #include +#include #include #include "dump_config/config_output.h" @@ -39,6 +40,8 @@ #include "utils/stats.h" #include "build.h" +#include "lua_bootstrap.h" +#include "lua_finalize.h" using namespace snort; using namespace std; @@ -77,12 +80,12 @@ static void install_version_strings(lua_State* L) } } - string Shell::fatal; std::stack Shell::current_shells; ConfigOutput* Shell::s_config_output = nullptr; BaseConfigNode* Shell::s_current_node = nullptr; bool Shell::s_close_table = true; +string Shell::lua_sandbox; // FIXIT-M Shell::panic() works on Linux but on OSX we can't throw from lua // to C++. unprotected lua calls could be wrapped in a pcall to ensure lua @@ -295,43 +298,90 @@ static int get_line_number(lua_State* L) #endif -static bool load_config(lua_State* L, const char* file, const char* tweaks, bool is_fatal) +void Shell::set_sandbox_env() { - Lua::ManageStack ms(L); - if ( luaL_loadfile(L, file) ) - { - if (is_fatal) - FatalError("can't load %s: %s\n", file, lua_tostring(L, -1)); - else - ParseError("can't load %s: %s\n", file, lua_tostring(L, -1)); - return false; - } - if ( tweaks and *tweaks ) + lua_getglobal(lua, "sandbox_env"); + + if ( lua_istable(lua, -1) ) { - lua_pushstring(L, tweaks); - lua_setglobal(L, "tweaks"); + if ( !lua_setfenv(lua, -2) ) + FatalError("can't set sandbox environment\n"); } + else + FatalError("sandbox environment not defined\n"); +} + +bool Shell::load_lua_sandbox() +{ + if (lua_sandbox.empty()) + return false; + + Lua::ManageStack ms(lua); + + LogMessage("Loading lua sandbox %s:\n", lua_sandbox.c_str()); + if ( luaL_loadfile(lua, lua_sandbox.c_str()) ) + FatalError("can't load lua sandbox %s: %s\n", lua_sandbox.c_str(), lua_tostring(lua, -1)); + + if ( lua_pcall(lua, 0, 0, 0) ) + FatalError("can't init lua sandbox %s: %s\n", lua_sandbox.c_str(), lua_tostring(lua, -1)); + LogMessage("Finished %s:\n", lua_sandbox.c_str()); + + lua_getglobal(lua, "sandbox_env"); + if ( !lua_istable(lua, -1) ) + FatalError("sandbox_env table doesn't exist in %s: %s\n", lua_sandbox.c_str(), + lua_tostring(lua, -1)); + + lua_getglobal(lua, "create_sandbox_env"); + if ( lua_pcall(lua, 0, 0, 0) != 0 ) + FatalError("can't create sandbox environment %s: %s\n", lua_sandbox.c_str(), + lua_tostring(lua, -1)); + + return true; +} + +bool Shell::load_string(const char* s, bool load_in_sandbox, const char* message) +{ + Lua::ManageStack ms(lua); + + if ( luaL_loadstring(lua, s) ) + FatalError("can't load %s: %s\n", message, lua_tostring(lua, -1)); - if ( lua_pcall(L, 0, 0, 0) ) - FatalError("can't init %s: %s\n", file, lua_tostring(L, -1)); + if ( load_in_sandbox ) + set_sandbox_env(); + + if ( lua_pcall(lua, 0, 0, 0) ) + FatalError("can't init %s: %s\n", message, lua_tostring(lua, -1)); return true; } -static void load_string(lua_State* L, const char* s) +bool Shell::load_config(const char* file, bool load_in_sandbox, bool is_fatal) { - Lua::ManageStack ms(L); + if ( load_in_sandbox ) + { + ifstream in_file(file, ifstream::in); + if (in_file.get() == 27 ) + FatalError("bytecode is not allowed %s\n", file); + } - if ( luaL_loadstring(L, s) ) + Lua::ManageStack ms(lua); + + if ( luaL_loadfile(lua, file) ) { - const char* err = lua_tostring(L, -1); - if ( strstr(err, "near '#'") ) - ParseError("this doesn't look like Lua. Comments start with --, not #."); - FatalError("can't load overrides: %s\n", err); + if (is_fatal) + FatalError("can't load %s: %s\n", file, lua_tostring(lua, -1)); + else + ParseError("can't load %s: %s\n", file, lua_tostring(lua, -1)); + return false; } - if ( lua_pcall(L, 0, 0, 0) ) - FatalError("can't init overrides: %s\n", lua_tostring(L, -1)); + if ( load_in_sandbox ) + set_sandbox_env(); + + if ( lua_pcall(lua, 0, 0, 0) ) + FatalError("can't init %s: %s\n", file, lua_tostring(lua, -1)); + + return true; } //------------------------------------------------------------------------- @@ -339,7 +389,7 @@ static void load_string(lua_State* L, const char* s) //------------------------------------------------------------------------- Shell::Shell(const char* s, bool load_defaults) : - config_data(s) + config_data(s), load_defaults(load_defaults) { // FIXIT-M should wrap in Lua::State lua = luaL_newstate(); @@ -358,13 +408,10 @@ Shell::Shell(const char* s, bool load_defaults) : parse_from = get_parse_file(); loaded = false; - load_string(lua, ModuleManager::get_lua_bootstrap()); + load_string(lua_bootstrap, false, "bootstrap"); install_version_strings(lua); bootstrapped = true; - if ( load_defaults ) - load_string(lua, ModuleManager::get_lua_coreinit()); - current_shells.pop(); } @@ -406,6 +453,17 @@ bool Shell::configure(SnortConfig* sc, bool is_fatal, bool is_root) set_network_policy(pt->network); } + if (!sc->tweaks.empty()) + { + lua_pushstring(lua, sc->tweaks.c_str()); + lua_setglobal(lua, "tweaks"); + } + + bool load_in_sandbox = load_lua_sandbox(); + + if ( load_defaults ) + load_string(ModuleManager::get_lua_coreinit(), load_in_sandbox, "coreinit"); + std::string path = parse_from; const char* code; @@ -430,19 +488,20 @@ bool Shell::configure(SnortConfig* sc, bool is_fatal, bool is_root) current_shells.push(this); - if (!path.empty() and !load_config(lua, path.c_str(), sc->tweaks.c_str(), is_fatal)) + if ( !path.empty() and + !load_config(path.c_str(), load_in_sandbox, is_fatal) ) { current_shells.pop(); return false; } if ( !overrides.empty() ) - load_string(lua, overrides.c_str()); + load_string(overrides.c_str(), load_in_sandbox, "overrides"); if ( SnortConfig::log_verbose() ) print_allowlist(); - load_string(lua, ModuleManager::get_lua_finalize()); + load_string(lua_finalize, false, "finalize"); clear_allowlist(); diff --git a/src/main/shell.h b/src/main/shell.h index bcab4e3b1..472157867 100644 --- a/src/main/shell.h +++ b/src/main/shell.h @@ -79,6 +79,9 @@ public: static void set_config_output(ConfigOutput* config_output); static void clear_config_output(); + static void set_lua_sandbox(const char* s) + { lua_sandbox = s; } + private: static void add_config_root_node(const std::string& root_name, snort::Parameter::Type type); @@ -92,6 +95,7 @@ private: static ConfigOutput* s_config_output; static BaseConfigNode* s_current_node; static bool s_close_table; + static std::string lua_sandbox; private: void clear_allowlist() @@ -113,6 +117,11 @@ private: void print_allowlist() const; void allowlist_update(const char* keyword, bool is_prefix); + bool load_lua_sandbox(); + void set_sandbox_env(); + bool load_string(const char* s, bool load_in_sandbox, const char* message); + bool load_config(const char* file, bool load_in_sandbox, bool is_fatal); + private: bool loaded; bool bootstrapped = false; @@ -124,6 +133,7 @@ private: Allowlist internal_allowlist; Allowlist allowlist_prefixes; ConfigData config_data; + bool load_defaults; }; #endif diff --git a/src/main/snort_module.cc b/src/main/snort_module.cc index edf693b6b..5f83814cc 100644 --- a/src/main/snort_module.cc +++ b/src/main/snort_module.cc @@ -439,6 +439,9 @@ static const Parameter s_params[] = { "--lua", Parameter::PT_STRING, nullptr, nullptr, " extend/override conf with chunk; may be repeated" }, + { "--lua-sandbox", Parameter::PT_STRING, nullptr, nullptr, + " file that contains the lua sandbox environment in which config will be loaded" }, + { "--logid", Parameter::PT_INT, "0:65535", nullptr, "<0xid> log Identifier to uniquely id events for multiple snorts (same as -G)" }, @@ -983,6 +986,9 @@ bool SnortModule::set(const char*, Value& v, SnortConfig* sc) else if ( v.is("--lua") ) sc->policy_map->get_shell()->set_overrides(v.get_string()); + else if ( v.is("--lua-sandbox") ) + Shell::set_lua_sandbox(v.get_string()); + else if ( v.is("--markup") ) config_markup(sc, v.get_string()); diff --git a/src/managers/CMakeLists.txt b/src/managers/CMakeLists.txt index 854f509d4..c5b024b82 100644 --- a/src/managers/CMakeLists.txt +++ b/src/managers/CMakeLists.txt @@ -2,14 +2,10 @@ set (LUA_INCLUDES # required 'header' ${CMAKE_CURRENT_BINARY_DIR}/snort_plugin.lua - # deprecated dependency to be removed with RC - ${CMAKE_CURRENT_BINARY_DIR}/snort_config.lua ) set (CPP_INCLUDES - ${CMAKE_CURRENT_BINARY_DIR}/lua_bootstrap.h ${CMAKE_CURRENT_BINARY_DIR}/lua_coreinit.h - ${CMAKE_CURRENT_BINARY_DIR}/lua_finalize.h ) set( MANAGERS_INCLUDES @@ -50,22 +46,9 @@ add_custom_command ( COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/plugffi.lua ${CMAKE_CURRENT_BINARY_DIR}/snort_plugin.lua ) -add_custom_command ( - OUTPUT lua_bootstrap.h snort_config.lua - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/lua_wrap.sh ${CMAKE_CURRENT_SOURCE_DIR} bootstrap > lua_bootstrap.h - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bootstrap.lua ${CMAKE_CURRENT_BINARY_DIR}/snort_config.lua - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bootstrap.lua -) - -add_custom_command ( - OUTPUT lua_finalize.h - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/lua_wrap.sh ${CMAKE_CURRENT_SOURCE_DIR} finalize > lua_finalize.h - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/finalize.lua -) - add_custom_command ( OUTPUT lua_coreinit.h - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/lua_wrap.sh ${CMAKE_CURRENT_SOURCE_DIR} coreinit > lua_coreinit.h + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/../lua_wrap.sh ${CMAKE_CURRENT_SOURCE_DIR} coreinit > lua_coreinit.h DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/coreinit.lua ) diff --git a/src/managers/module_manager.cc b/src/managers/module_manager.cc index 4b3988b1b..2cab9293d 100644 --- a/src/managers/module_manager.cc +++ b/src/managers/module_manager.cc @@ -51,9 +51,7 @@ #include "plugin_manager.h" // "Lua" includes -#include "lua_bootstrap.h" #include "lua_coreinit.h" -#include "lua_finalize.h" using namespace snort; using namespace std; @@ -106,12 +104,6 @@ extern "C" // boot foo //------------------------------------------------------------------------- -const char* ModuleManager::get_lua_bootstrap() -{ return lua_bootstrap; } - -const char* ModuleManager::get_lua_finalize() -{ return lua_finalize; } - const char* ModuleManager::get_lua_coreinit() { return lua_coreinit; } diff --git a/src/managers/module_manager.h b/src/managers/module_manager.h index 38b2c6374..fc0d53b8d 100644 --- a/src/managers/module_manager.h +++ b/src/managers/module_manager.h @@ -51,8 +51,6 @@ public: static Module* get_default_module(const char*, SnortConfig*); SO_PUBLIC static std::list get_all_modules(); - static const char* get_lua_bootstrap(); - static const char* get_lua_finalize(); static const char* get_lua_coreinit(); static void list_modules(const char* = nullptr);