summaryrefslogtreecommitdiff
path: root/lib/supple/sandbox.lua
blob: 3c807cb5eabe4243f45163df67bc884099f12013 (plain)
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
-- 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

-- Run fn with globs as its globals.  Returns a function to run which
-- returns the return values of fn, and also wrap returns the table
-- which will be filled with any new globals fn creates.
--
-- If fn is a string then it is used as the source of the function, if it
-- is a function then it is expected to be a closure which when called 
-- repeatedly returns more data loaded (perhaps from a file?).  src is
-- expected to be the name associated with this source code.
--
-- In case of error, returns nil, errmsg
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 run()
   -- Run the sandbox
   local result, errno = capi.lockdown()

   if 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

-- START_TEST_ONLY
   if result ~= "oknonroot" then
-- END_TEST_ONLY
      -- Check that we're definitely solidly jailed
      fh, errno = sio.open("testfile", "rw")
      if fh then
	 fh:close()
	 luxio.unlink("testfile")
	 return 1
      end
-- START_TEST_ONLY
   end
-- END_TEST_ONLY
   
   -- Prepare a severely limited sandbox
   local sandbox_globals = {
      type = capi.type,
   }

   for _, k in ipairs({ "table", "string", "pairs", "ipairs", "pcall",
			 "xpcall", "unpack", "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(wrappered_load, "supple:loadstring")
   objects.give(objects.clean_down, "supple:clean_down")
   comms._set_fd(0)

   return fn(comms._wait)
end

return {
   run = run,
}