1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
-- lib/supple/sandbox.lua
--
-- Sandbox (for) Untrusted Procedure Partitioning (in) Lua Engine
--
-- Code which runs the core sandbox functionality of supple.
--
-- This module runs in the sandbox interpreter which means some of the code
-- runs with root access. As such, we minimise what can be done before
-- we drop privileges.
--
-- The wrapper used to run us already ensured that LUA_PATH etc are
-- not set, so we don't have to worry about non-system-installed modules
-- getting in our way. Once the supple libraries are loaded (which will
-- include loading luxio etc) we're good to go and can ask the supple.capi
-- module to lock us down.
--
-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org>
--
-- For licence terms, see COPYING
--
local capi = require 'supple.capi'
local objects = require 'supple.objects'
local comms = require 'supple.comms'
local luxio = require 'luxio'
local sio = require 'luxio.simple'
local loadstring = loadstring
local load = load
local setfenv = setfenv
local gc = collectgarbage
local unpack = unpack
local function set_limits(ltab)
local count = ltab.count
local memory = ltab.memory
local limits = { count = count, memory = memory}
if limits.memory then
-- Bump memory limit by current usage to be kinder
limits.memory = limits.memory + (gc "count")
end
if not limits.count and not limits.memory then
return false, "Expected an opcode count or total memory limit"
end
comms.set_limits(limits)
return true
end
local function _wrap(fn, src, globs)
globs = globs or {}
local fn_glob = setmetatable({}, { __index = globs, __metatable=true })
local fn_ret, msg
assert(fn, "No function/source provided?")
assert(src, "No source name provided?")
if setfenv then
-- Lua 5.1 style load...
fn_ret, msg = ((capi.rawtype(fn) == "string") and loadstring or load)(fn, src)
if not fn_ret then
return nil, msg
end
setfenv(fn_ret, fn_glob)
else
-- Lua 5.2 style load...
fn_ret, msg = load(fn, src, "t", fn_glob)
if not fn_ret then
return nil, msg
end
end
assert(fn_ret, "Unusual, missing fn_ret now?")
return fn_ret, fn_glob
end
local function wrapped_unpack(t)
local packed = t
if capi.rawtype(t) ~= "table" then
local len = #t
packed = {}
for i = 1, len do
packed[i] = t[i]
end
end
return unpack(packed)
end
local function run()
-- Run the sandbox
local result, errno = capi.lockdown()
if result ~= "ok" and result ~= "OK"
-- START_TEST_ONLY
and result ~= "oknonroot"
-- END_TEST_ONLY
then
-- Failure to sandbox, so abort
print(result, luxio.strerror(errno))
return errno
end
if result == "ok" then
-- Note, if result is "OK" then we're pretty hard jailed
-- and cannot even do this test lest we get killed
-- Check that we're definitely solidly jailed
fh, errno = sio.open("testfile", "rw")
if fh then
fh:close()
luxio.unlink("testfile")
return 1
end
end
-- Prepare a severely limited sandbox
local sandbox_globals = {
type = capi.type,
pairs = capi.pairs,
ipairs = capi.ipairs,
next = capi.next,
unpack = wrapped_unpack,
}
for _, k in ipairs({ "table", "string", "pcall",
"xpcall", "tostring", "tonumber", "math",
"coroutine", "select", "error", "assert" }) do
sandbox_globals[k] = _G[k]
end
-- Complete its "globals"
sandbox_globals._G = sandbox_globals
local _go_str = [[
return ({...})[1]()
]]
local fn, globs = _wrap(_go_str, "sandbox", sandbox_globals)
if not fn then
return 1
end
objects.set_name(("supple-sandbox[%d,%%d]"):format(luxio.getpid()))
objects.set_proc_call(comms.call)
local function wrappered_load(str, name)
return _wrap(str, name, sandbox_globals)
end
-- Pretend we've "given" the host an object called 'supple:loadstring'
-- which is the loadstring/load function
objects.give(set_limits, "supple:set_limits")
objects.give(wrappered_load, "supple:loadstring")
objects.give(objects.clean_down, "supple:clean_down")
comms._set_fd(0)
return fn(comms._wait)
end
return {
run = run,
}
|