mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2026-05-03 18:28:01 +02:00
wplua: implement sandboxing of scripts
All loaded scripts can now be run in a protected environment so that they cannot do any harm to the host or to each other
This commit is contained in:
parent
b9ca4e1425
commit
8b4c5af49c
7 changed files with 189 additions and 9 deletions
6
lib/wplua/gresource.xml
Normal file
6
lib/wplua/gresource.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/org/freedesktop/pipewire/wireplumber/wplua/">
|
||||
<file compressed="true">sandbox.lua</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
|
|
@ -7,8 +7,15 @@ wplua_lib_sources = [
|
|||
'wplua.c',
|
||||
]
|
||||
|
||||
wplua_resources = gnome.compile_resources(
|
||||
'wplua-resources',
|
||||
'gresource.xml',
|
||||
c_name: '_wplua',
|
||||
extra_args: '--manual-register',
|
||||
source_dir: meson.current_source_dir())
|
||||
|
||||
wplua_lib = static_library('wplua-' + wireplumber_api_version,
|
||||
wplua_lib_sources,
|
||||
[ wplua_lib_sources, wplua_resources ],
|
||||
c_args : [
|
||||
'-D_GNU_SOURCE',
|
||||
'-DG_LOG_USE_STRUCTURED',
|
||||
|
|
|
|||
75
lib/wplua/sandbox.lua
Normal file
75
lib/wplua/sandbox.lua
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
-- WirePlumber
|
||||
--
|
||||
-- Copyright © 2020 Collabora Ltd.
|
||||
-- @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||
--
|
||||
-- Based on https://github.com/kikito/sandbox.lua
|
||||
-- Copyright © 2013 Enrique García Cota
|
||||
--
|
||||
-- SPDX-License-Identifier: MIT
|
||||
|
||||
SANDBOX_ENV_LIST = {}
|
||||
local SANDBOX_ENV = {}
|
||||
|
||||
-- List of safe functions and packages
|
||||
([[
|
||||
|
||||
_VERSION assert error ipairs next pairs
|
||||
pcall select tonumber tostring type xpcall
|
||||
|
||||
table utf8
|
||||
|
||||
math.abs math.acos math.asin math.atan math.ceil
|
||||
math.cos math.deg math.exp math.tointeger math.floor math.fmod
|
||||
math.huge math.ult math.log math.maxinteger math.mininteger math.max
|
||||
math.min math.modf math.pi math.rad math.random
|
||||
math.sin math.sqrt math.tan math.type
|
||||
|
||||
string.byte string.char string.find string.format string.gmatch
|
||||
string.gsub string.len string.lower string.match string.reverse
|
||||
string.sub string.upper
|
||||
|
||||
os.clock os.difftime os.time os.date os.getenv
|
||||
|
||||
]]):gsub('%S+', function(id)
|
||||
local module, method = id:match('([^%.]+)%.([^%.]+)')
|
||||
if module then
|
||||
SANDBOX_ENV[module] = SANDBOX_ENV[module] or {}
|
||||
SANDBOX_ENV[module][method] = _G[module][method]
|
||||
else
|
||||
SANDBOX_ENV[id] = _G[id]
|
||||
end
|
||||
end)
|
||||
|
||||
-- Additionally export everything in SANDBOX_EXPORT
|
||||
if type(SANDBOX_EXPORT) == "table" then
|
||||
for k, v in pairs(SANDBOX_EXPORT) do
|
||||
SANDBOX_ENV[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
-- Additionally protect packages from malicious scripts trying to override methods
|
||||
for k, v in pairs(SANDBOX_ENV) do
|
||||
if type(v) == "table" then
|
||||
SANDBOX_ENV[k] = setmetatable({}, {
|
||||
__index = v,
|
||||
__newindex = function(_, attr_name, _)
|
||||
error('Can not modify ' .. k .. '.' .. attr_name .. '. Protected by the sandbox.')
|
||||
end
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function sandbox(chunk)
|
||||
-- chunk's environment will be an empty table with __index
|
||||
-- to access our SANDBOX_ENV (without being able to write it)
|
||||
local env = setmetatable({}, {
|
||||
__index = SANDBOX_ENV,
|
||||
})
|
||||
-- set it as the chunk's 1st upvalue (__ENV)
|
||||
debug.setupvalue(chunk, 1, env)
|
||||
-- store the chunk's environment so that it is not garbage collected
|
||||
table.insert(SANDBOX_ENV_LIST, env)
|
||||
-- execute the chunk
|
||||
chunk()
|
||||
end
|
||||
|
|
@ -10,6 +10,10 @@
|
|||
#include "private.h"
|
||||
#include <wp/wp.h>
|
||||
|
||||
#define URI_SANDBOX "resource:///org/freedesktop/pipewire/wireplumber/wplua/sandbox.lua"
|
||||
|
||||
extern void _wplua_register_resource (void);
|
||||
|
||||
static void
|
||||
_wplua_openlibs (lua_State *L)
|
||||
{
|
||||
|
|
@ -21,7 +25,7 @@ _wplua_openlibs (lua_State *L)
|
|||
/* {LUA_COLIBNAME, luaopen_coroutine}, */
|
||||
{LUA_TABLIBNAME, luaopen_table},
|
||||
/* {LUA_IOLIBNAME, luaopen_io}, */
|
||||
/* {LUA_OSLIBNAME, luaopen_os}, */
|
||||
{LUA_OSLIBNAME, luaopen_os},
|
||||
{LUA_STRLIBNAME, luaopen_string},
|
||||
{LUA_MATHLIBNAME, luaopen_math},
|
||||
{LUA_UTF8LIBNAME, luaopen_utf8},
|
||||
|
|
@ -53,10 +57,16 @@ _wplua_typeclass___call (lua_State *L)
|
|||
lua_State *
|
||||
wplua_new (void)
|
||||
{
|
||||
static gboolean resource_registered = FALSE;
|
||||
lua_State *L = luaL_newstate ();
|
||||
|
||||
wp_debug ("initializing lua_State %p", L);
|
||||
|
||||
if (!resource_registered) {
|
||||
_wplua_register_resource ();
|
||||
resource_registered = TRUE;
|
||||
}
|
||||
|
||||
_wplua_openlibs (L);
|
||||
_wplua_init_gboxed (L);
|
||||
_wplua_init_gobject (L);
|
||||
|
|
@ -89,6 +99,16 @@ wplua_free (lua_State * L)
|
|||
lua_close (L);
|
||||
}
|
||||
|
||||
void
|
||||
wplua_enable_sandbox (lua_State * L)
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
wp_debug ("enabling Lua sandbox");
|
||||
if (!wplua_load_uri (L, URI_SANDBOX, &error)) {
|
||||
wp_critical ("Failed to load sandbox: %s", error->message);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
wplua_register_type_methods (lua_State * L, GType type,
|
||||
lua_CFunction constructor, const luaL_Reg * methods)
|
||||
|
|
@ -128,19 +148,28 @@ wplua_register_type_methods (lua_State * L, GType type,
|
|||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
static gboolean
|
||||
_wplua_load_buffer (lua_State * L, const gchar *buf, gsize size,
|
||||
const gchar * name, GError **error)
|
||||
{
|
||||
int ret;ret = luaL_loadbuffer (L, buf, size, name);
|
||||
int ret;
|
||||
int sandbox = 0;
|
||||
|
||||
/* wrap with sandbox() if it's loaded */
|
||||
if (lua_getglobal (L, "sandbox") == LUA_TFUNCTION)
|
||||
sandbox = 1;
|
||||
else
|
||||
lua_pop (L, 1);
|
||||
|
||||
ret = luaL_loadbuffer (L, buf, size, name);
|
||||
if (ret != LUA_OK) {
|
||||
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"Failed to compile: %s", lua_tostring (L, -1));
|
||||
lua_pop (L, 1);
|
||||
lua_pop (L, sandbox + 1);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ret = lua_pcall (L, 0, 0, 0);
|
||||
ret = lua_pcall (L, sandbox, 0, 0);
|
||||
if (ret != LUA_OK) {
|
||||
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"Failed to run: %s", lua_tostring (L, -1));
|
||||
|
|
@ -177,8 +206,8 @@ wplua_load_uri (lua_State * L, const gchar *uri, GError **error)
|
|||
|
||||
file = g_file_new_for_uri (uri);
|
||||
if (!(bytes = g_file_load_bytes (file, NULL, NULL, &err))) {
|
||||
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"Failed to load '%s': %s", uri, err->message);
|
||||
g_propagate_prefixed_error (error, err, "Failed to load '%s':", uri);
|
||||
err = NULL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ G_BEGIN_DECLS
|
|||
lua_State * wplua_new (void);
|
||||
void wplua_free (lua_State * L);
|
||||
|
||||
void wplua_enable_sandbox (lua_State * L);
|
||||
|
||||
void wplua_register_type_methods (lua_State * L, GType type,
|
||||
lua_CFunction constructor, const luaL_Reg * methods);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ common_env = [
|
|||
|
||||
test(
|
||||
'test-wplua',
|
||||
executable('test-toml', 'wplua.c', dependencies: common_deps),
|
||||
executable('test-wplua', 'wplua.c', dependencies: common_deps),
|
||||
env: common_env,
|
||||
workdir : meson.current_source_dir(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -403,6 +403,66 @@ test_wplua_signals ()
|
|||
wplua_free (L);
|
||||
}
|
||||
|
||||
static void
|
||||
test_wplua_sandbox ()
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
lua_State *L = wplua_new ();
|
||||
|
||||
wplua_register_type_methods(L, TEST_TYPE_OBJECT,
|
||||
l_test_object_new, l_test_object_methods);
|
||||
|
||||
const gchar code[] =
|
||||
"SANDBOX_EXPORT = {\n"
|
||||
" Test = TestObject.new,\n"
|
||||
" Table = { test = 'foobar' }\n"
|
||||
"}\n";
|
||||
wplua_load_buffer (L, code, sizeof (code) - 1, &error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
wplua_enable_sandbox (L);
|
||||
|
||||
const gchar code2[] =
|
||||
"o = TestObject.new()\n";
|
||||
wplua_load_buffer (L, code2, sizeof (code2) - 1, &error);
|
||||
g_debug ("expected error: %s", error ? error->message : "null");
|
||||
g_assert_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED);
|
||||
g_clear_error (&error);
|
||||
|
||||
const gchar code3[] =
|
||||
"o = Test()\n";
|
||||
wplua_load_buffer (L, code3, sizeof (code3) - 1, &error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
const gchar code4[] =
|
||||
"assert(string.len(Table.test) == 6)\n";
|
||||
wplua_load_buffer (L, code4, sizeof (code4) - 1, &error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
const gchar code5[] =
|
||||
"o:call('change', 'by Lua', 55)\n";
|
||||
wplua_load_buffer (L, code5, sizeof (code5) - 1, &error);
|
||||
g_debug ("expected error: %s", error ? error->message : "null");
|
||||
g_assert_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED);
|
||||
g_clear_error (&error);
|
||||
|
||||
const gchar code6[] =
|
||||
"string.test = 'hello world'\n";
|
||||
wplua_load_buffer (L, code6, sizeof (code6) - 1, &error);
|
||||
g_debug ("expected error: %s", error ? error->message : "null");
|
||||
g_assert_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED);
|
||||
g_clear_error (&error);
|
||||
|
||||
const gchar code7[] =
|
||||
"Table.test = 'hello world'\n";
|
||||
wplua_load_buffer (L, code7, sizeof (code7) - 1, &error);
|
||||
g_debug ("expected error: %s", error ? error->message : "null");
|
||||
g_assert_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED);
|
||||
g_clear_error (&error);
|
||||
|
||||
wplua_free (L);
|
||||
}
|
||||
|
||||
gint
|
||||
main (gint argc, gchar *argv[])
|
||||
{
|
||||
|
|
@ -414,6 +474,7 @@ main (gint argc, gchar *argv[])
|
|||
g_test_add_func ("/wplua/properties", test_wplua_properties);
|
||||
g_test_add_func ("/wplua/closure", test_wplua_closure);
|
||||
g_test_add_func ("/wplua/signals", test_wplua_signals);
|
||||
g_test_add_func ("/wplua/sandbox", test_wplua_sandbox);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue