]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2608 in SNORT/snort3 from ~SHRARANG/snort3:lua_sandbox to master
authorShravan Rangarajuvenkata (shrarang) <shrarang@cisco.com>
Wed, 2 Dec 2020 17:43:31 +0000 (17:43 +0000)
committerShravan Rangarajuvenkata (shrarang) <shrarang@cisco.com>
Wed, 2 Dec 2020 17:43:31 +0000 (17:43 +0000)
Squashed commit of the following:

commit bd0c2a888b69a9791bb2b8dd196c8a6fdd53ca1c
Author: Shravan Rangaraju <shrarang@cisco.com>
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.

src/host_tracker/test/host_cache_module_test.cc
src/lua_wrap.sh [moved from src/managers/lua_wrap.sh with 100% similarity]
src/main/CMakeLists.txt
src/main/bootstrap.lua [moved from src/managers/bootstrap.lua with 73% similarity]
src/main/finalize.lua [moved from src/managers/finalize.lua with 88% similarity]
src/main/shell.cc
src/main/shell.h
src/main/snort_module.cc
src/managers/CMakeLists.txt
src/managers/module_manager.cc
src/managers/module_manager.h

index 567452cdcdce2e45c67dda17da78e715262210e2..c730144b3e10677da84b4d23d07880c77ab20c84 100644 (file)
@@ -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*) { }
similarity index 100%
rename from src/managers/lua_wrap.sh
rename to src/lua_wrap.sh
index 30b0f6adda1fade083e79fee5653220b5754abe8..95aa8a4655ced8299e5021543f472bc79264ca37 100644 (file)
@@ -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"
 )
similarity index 73%
rename from src/managers/bootstrap.lua
rename to src/main/bootstrap.lua
index 17b20ea0ea77f4b12240fda18dfe4890627e9933..e1d5af7141aa1f310e2b44cd1f68df035d6806d5 100644 (file)
@@ -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
similarity index 88%
rename from src/managers/finalize.lua
rename to src/main/finalize.lua
index 47d3e0266bcf0fc175efc3759bdb8abf789f194e..83d2df7a8a8f959e6abdb6bdb82a0c46d67406a2 100644 (file)
@@ -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
index da8386ba8d14f7f6d99a11cdcdb3647f62af5ebc..c66cab4520f1280026423248bd81570785582ebf 100644 (file)
@@ -26,6 +26,7 @@
 #include <unistd.h>
 
 #include <cassert>
+#include <fstream>
 #include <stdexcept>
 
 #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*> 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();
 
index bcab4e3b1a55cfab6b7f50ef9d55d2d65c13e4ba..472157867817b891118ab6412c19c5597ac2c6b9 100644 (file)
@@ -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
index edf693b6b47ca067f1a6c78319129894077b69d3..5f83814ccde5d840b291a407bfc0f31d1cba884e 100644 (file)
@@ -439,6 +439,9 @@ static const Parameter s_params[] =
     { "--lua", Parameter::PT_STRING, nullptr, nullptr,
       "<chunk> extend/override conf with chunk; may be repeated" },
 
+    { "--lua-sandbox", Parameter::PT_STRING, nullptr, nullptr,
+      "<file> 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());
 
index 854f509d405c2dd4c38e8e2397f4fbc611385408..c5b024b824814a080f6d31fe47d90a858bcb844c 100644 (file)
@@ -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
 )
 
index 4b3988b1bd4a1e449b10d4c9079bdd680f0632fa..2cab9293dbee42035b2b8a99ce41c98f4782e986 100644 (file)
@@ -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; }
 
index 38b2c63740fe43ca89484bb8c9a5c619d106c5ed..fc0d53b8d1d0496e129846c56a341df513305429 100644 (file)
@@ -51,8 +51,6 @@ public:
     static Module* get_default_module(const char*, SnortConfig*);
     SO_PUBLIC static std::list<Module*> 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);