Explicitly sandbox Lua instances (3/4)

This allows UI scripts (saved in preferences) to access
os.* functions (non-sandboxed), while preventing other
scripts to do so.

Lua scripts that can run os commands can execute arbitrary
code on the system. While this is a nice feature, it can be
equally dangerous.
This commit is contained in:
Robin Gareus 2023-10-04 02:07:13 +02:00
parent 6b3f25eb2a
commit c1be897eed
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
9 changed files with 46 additions and 40 deletions

View File

@ -2350,12 +2350,11 @@ ARDOUR_UI::route_setup_info (const std::string& script_path)
return rv;
}
LuaState lua;
LuaState lua (true, true);
lua.Print.connect (&_lua_print);
lua.sandbox (true);
lua_State* L = lua.getState();
LuaInstance::register_classes (L);
LuaInstance::register_classes (L, true);
LuaBindings::set_session (L, _session);
luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
lua_setglobal (L, "Editor");
@ -2403,12 +2402,11 @@ ARDOUR_UI::meta_route_setup (const std::string& script_path)
return;
}
LuaState lua;
LuaState lua (true, true);
lua.Print.connect (&_lua_print);
lua.sandbox (true);
lua_State* L = lua.getState();
LuaInstance::register_classes (L);
LuaInstance::register_classes (L, true);
LuaBindings::set_session (L, _session);
luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
lua_setglobal (L, "Editor");
@ -2449,12 +2447,13 @@ ARDOUR_UI::meta_session_setup (const std::string& script_path)
return;
}
LuaState lua;
bool sandbox = UIConfiguration::instance().get_sandbox_all_lua_scripts ();
LuaState lua (true, sandbox);
lua.Print.connect (&_lua_print);
lua.sandbox (false);
lua_State* L = lua.getState();
LuaInstance::register_classes (L);
LuaInstance::register_classes (L, sandbox);
LuaBindings::set_session (L, _session);
luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
lua_setglobal (L, "Editor");

View File

@ -941,11 +941,16 @@ Editor::trigger_script_by_name (const std::string script_name, const std::string
return;
}
LuaState lua;
#ifdef MIXBUS
bool sandbox = false; // mixer state save/reset/restore needs os.*
#else
bool sandbox = UIConfiguration::instance().get_sandbox_all_lua_scripts ();
#endif
LuaState lua (true, sandbox);
lua.Print.connect (&_lua_print);
lua.sandbox (false);
lua_State* L = lua.getState();
LuaInstance::register_classes (L);
LuaInstance::register_classes (L, sandbox);
LuaBindings::set_session (L, _session);
luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
lua_setglobal (L, "Editor");

View File

@ -33,7 +33,7 @@ int main (int argc, char **argv)
{
#ifdef LUABINDINGDOC
luabridge::setPrintBindings (true);
LuaState lua;
LuaState lua (false, false);
lua_State* L = lua.getState ();
#ifdef LUADOCOUT
printf ("-- %s\n", ARDOUR::revision);
@ -42,7 +42,7 @@ int main (int argc, char **argv)
printf ("[\n");
printf ("{\"version\" : \"%s\"},\n\n", ARDOUR::revision);
#endif
LuaInstance::register_classes (L);
LuaInstance::register_classes (L, false);
LuaInstance::register_hooks (L);
ARDOUR::LuaBindings::dsp (L);
#ifdef LUADOCOUT

View File

@ -778,7 +778,7 @@ LuaInstance::bind_dialog (lua_State* L)
}
void
LuaInstance::register_classes (lua_State* L)
LuaInstance::register_classes (lua_State* L, bool sandbox)
{
LuaBindings::stddef (L);
LuaBindings::common (L);
@ -1100,15 +1100,7 @@ LuaInstance::register_classes (lua_State* L)
.addFunction ("config", &_ui_config)
.endNamespace () // end ArdourUI
.beginNamespace ("os")
#ifndef PLATFORM_WINDOWS
.addFunction ("execute", &lua_exec)
#endif
.addCFunction ("forkexec", &lua_forkexec)
.endNamespace ();
.endNamespace (); // end ArdourUI
// Editing Symbols
#undef ZOOMFOCUS
@ -1135,6 +1127,16 @@ LuaInstance::register_classes (lua_State* L)
.beginNamespace ("Editing")
# include "editing_syms.h"
.endNamespace ();
if (!sandbox) {
luabridge::getGlobalNamespace (L)
.beginNamespace ("os")
#ifndef PLATFORM_WINDOWS
.addFunction ("execute", &lua_exec)
#endif
.addCFunction ("forkexec", &lua_forkexec)
.endNamespace ();
}
}
#undef xstr
@ -1174,6 +1176,7 @@ LuaInstance::destroy_instance ()
}
LuaInstance::LuaInstance ()
: lua (true, UIConfiguration::instance().get_sandbox_all_lua_scripts ())
{
lua.Print.connect (&_lua_print);
init ();
@ -1196,7 +1199,6 @@ LuaInstance::~LuaInstance ()
void
LuaInstance::init ()
{
lua.sandbox (false);
lua.do_command (
"function ScriptManager ()"
" local self = { scripts = {}, instances = {}, icons = {} }"
@ -1343,7 +1345,7 @@ LuaInstance::init ()
abort(); /*NOTREACHED*/
}
register_classes (L);
LuaInstance::register_classes (L, UIConfiguration::instance().get_sandbox_all_lua_scripts ());
register_hooks (L);
luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
@ -1527,8 +1529,8 @@ LuaInstance::pre_seed_script (std::string const& name, int& id)
if (spi) {
try {
std::string script = Glib::file_get_contents (spi->path);
LuaState ls;
register_classes (ls.getState ());
LuaState ls (true, true);
register_classes (ls.getState (), true);
LuaScriptParamList lsp = LuaScriptParams::script_params (ls, spi->path, "action_params");
LuaScriptParamPtr lspp (new LuaScriptParam("x-script-origin", "", spi->path, false, true));
lsp.push_back (lspp);
@ -1595,8 +1597,8 @@ LuaInstance::interactive_add (Gtk::Window& parent, LuaScriptInfo::ScriptType typ
return false;
}
LuaState ls;
register_classes (ls.getState ());
LuaState ls (true, true);
register_classes (ls.getState (), true);
LuaScriptParamList lsp = LuaScriptParams::script_params (ls, spi->path, param_function);
/* allow cancel */
@ -1865,9 +1867,8 @@ LuaInstance::register_lua_slot (const std::string& name, const std::string& scri
/* parse script, get ActionHook(s) from script */
ActionHook ah;
try {
LuaState l;
LuaState l (true, true);
l.Print.connect (&_lua_print);
l.sandbox (true);
lua_State* L = l.getState();
register_hooks (L);
l.do_command ("function ardour () end");
@ -1972,6 +1973,7 @@ LuaCallback::LuaCallback (Session *s,
const ActionHook& ah,
const ARDOUR::LuaScriptParamList& args)
: SessionHandlePtr (s)
, lua (true, UIConfiguration::instance().get_sandbox_all_lua_scripts ())
, _id ("0")
, _name (name)
, _signals (ah)
@ -2005,6 +2007,7 @@ LuaCallback::LuaCallback (Session *s,
LuaCallback::LuaCallback (Session *s, XMLNode & node)
: SessionHandlePtr (s)
, lua (true, UIConfiguration::instance().get_sandbox_all_lua_scripts ())
{
XMLNode* child = NULL;
if (node.name() != X_("LuaCallback")
@ -2088,7 +2091,6 @@ void
LuaCallback::init (void)
{
lua.Print.connect (&_lua_print);
lua.sandbox (false);
lua.do_command (
"function ScriptManager ()"
@ -2209,7 +2211,7 @@ LuaCallback::init (void)
abort(); /*NOTREACHED*/
}
LuaInstance::register_classes (L);
LuaInstance::register_classes (L, UIConfiguration::instance().get_sandbox_all_lua_scripts ());
LuaInstance::register_hooks (L);
luabridge::push <PublicEditor *> (L, &PublicEditor::instance());

View File

@ -112,7 +112,7 @@ public:
static void destroy_instance();
~LuaInstance();
static void register_classes (lua_State* L);
static void register_classes (lua_State* L, bool sandbox);
static void register_hooks (lua_State* L);
static void bind_cairo (lua_State* L);
static void bind_dialog (lua_State* L);

View File

@ -197,12 +197,11 @@ void LuaWindow::reinit_lua ()
{
ENSURE_GUI_THREAD (*this, &LuaWindow::session_going_away);
delete lua;
lua = new LuaState();
lua = new LuaState (true, UIConfiguration::instance().get_sandbox_all_lua_scripts ());
lua->Print.connect (sigc::mem_fun (*this, &LuaWindow::append_text));
lua->sandbox (false);
lua_State* L = lua->getState();
LuaInstance::register_classes (L);
LuaInstance::register_classes (L, UIConfiguration::instance().get_sandbox_all_lua_scripts ());
luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
lua_setglobal (L, "Editor");
}

View File

@ -1869,6 +1869,7 @@ ProcessorEntry::PluginInlineDisplay::display_frame (cairo_t* cr, double w, doubl
ProcessorEntry::LuaPluginDisplay::LuaPluginDisplay (ProcessorEntry& e, std::shared_ptr<ARDOUR::LuaProc> p, uint32_t max_height)
: PluginInlineDisplay (e, p, max_height)
, _luaproc (p)
, lua_gui (true, true)
, _lua_render_inline (0)
{
p->setup_lua_inline_gui (&lua_gui);

View File

@ -189,8 +189,7 @@ SessionDialog::meta_master_bus_profile (std::string script_path)
return UINT32_MAX;
}
LuaState lua;
lua.sandbox (true);
LuaState lua (true, true);
lua_State* L = lua.getState();
lua.do_command (

View File

@ -155,6 +155,7 @@ UI_CONFIG_VARIABLE (bool, ask_cut_copy_section_tempo_map, "ask-cut-copy-section-
UI_CONFIG_VARIABLE (std::string, freesound_dir, "freesound-dir", "")
UI_CONFIG_VARIABLE (int, max_note_height, "max-note-height", 20)
UI_CONFIG_VARIABLE (bool, prefer_tap_tempo, "prefer-tap-tempo", false)
UI_CONFIG_VARIABLE (bool, sandbox_all_lua_scripts, "sandbox-all-lua-scripts", false)
/* these are visibility-type selections in the New Track dialog that we should make persistent for the user's choices */
UI_CONFIG_VARIABLE (bool, show_on_cue_page, "show-on-cue-page", true)