This file is indexed.

/usr/share/lua/5.1/supple/sandbox.lua is in lua-supple 1.0.8-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
-- 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
--

--- Running code in sandboxes
--
-- This module is used by the sandbox code itself to start running sandboxed
-- Lua.  The only entry point is invoked by the sandbox C wrapper during
-- startup.  From here the sandbox locks itself down and then begins to listen
-- for work to do.
--
-- You should only need to interact with this module if you are writing your
-- own Supple wrapper binary to use instead of the provided wrapper.
--

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 track = require 'supple.track'

local loadstring = loadstring
local load = load
local setfenv = setfenv
local gc = collectgarbage
local unpack = unpack
local xpcall = xpcall
local traceback = debug.traceback

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, dont_wrap_globs)
   local fn_ret, msg

   if not dont_wrap_globs then
      globs = setmetatable({}, { __index = globs, __metatable = true })
   end

   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, globs)
   else
      -- Lua 5.2 style load...
      fn_ret, msg = load(fn, src, "t", globs)
      if not fn_ret then
	 return nil, msg
      end
   end

   assert(fn_ret, "Unusual, missing fn_ret now?")

   return fn_ret, globs
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

--- Start the sandbox running
--
-- This routine is invoked by the sandbox wrapper C code and starts the sandbox
-- running.  Approximately it locks the sandbox down, including various limits
-- such as chroot, rlimits, dropping privileges, and seccomp mode if available.
-- Then it begins the main RPC loop for the sandbox.
--
-- @function run
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 dont_wrap_globs = true
   local function wrappered_load(str, name)
      local dwg = dont_wrap_globs
      dont_wrap_globs = false
      return _wrap(str, name, sandbox_globals, dwg)
   end

   -- Pretend we've "given" the host an object called 'supple:loadstring'
   -- which is the loadstring/load function
   track.start()
   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

local function wrapper_run()
   local ok, retcode = xpcall(run, traceback)
   if not ok then
      error(retcode)
   end
   return retcode
end

return {
   run = wrapper_run,
}