summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
authorTimotej Lazar <timotej.lazar@araneo.org>2015-10-07 17:25:35 +0200
committerTimotej Lazar <timotej.lazar@araneo.org>2015-10-07 17:25:35 +0200
commit76cbfe9d620ca66a374b828c011c937918f80c2c (patch)
tree8ee165821df9ab46fda38869d1ae856be4efd2c7 /server
parentb7b4979f03f4d06919e251cfcc24642ccf9407ad (diff)
Add a sandbox for Python interpreter
Switch to user "nobody" and set additional limits.
Diffstat (limited to 'server')
-rw-r--r--server/python_session.py28
1 files changed, 20 insertions, 8 deletions
diff --git a/server/python_session.py b/server/python_session.py
index 24d33b0..f4c482c 100644
--- a/server/python_session.py
+++ b/server/python_session.py
@@ -116,15 +116,27 @@ class PythonSession(server.LanguageSession):
self._sent_hints.extend(hints)
def _interpreter(control, callback):
- directory = os.path.dirname(os.path.realpath(__file__))
- # TODO drop privileges using a wrapper
- script = os.path.join(directory, '..', 'python', 'interpreter.py')
+ basedir = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]
+ script = os.path.join(basedir, 'python', 'runner', 'interpreter.py')
+
+ # If the sandbox wrapper exists, use it to switch to user "nobody" and
+ # enforce additional limits. Unless the daemon is running as root we are
+ # not able to signal nobody's PIDs, so switch user again for the killing.
+ sandbox = os.path.join(basedir, 'python', 'runner', 'sandbox')
+ terminator = os.path.join(basedir, 'python', 'runner', 'terminator')
+ if os.path.exists(sandbox) and os.path.exists(terminator):
+ newuser = 'nobody' # TODO make this configurable
+ args = [sandbox, newuser, script]
+ kill = lambda proc, sig: subprocess.call([terminator, newuser, str(proc.pid), str(sig)])
+ else:
+ args = [script]
+ kill = lambda proc, sig: proc.send_signal(sig)
proc = None
while True:
# Ensure the interpreter process is running.
if proc is None:
- proc = subprocess.Popen([script],
+ proc = subprocess.Popen(args,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
@@ -143,7 +155,7 @@ def _interpreter(control, callback):
proc.stdin.write(data.encode('utf-8'))
proc.stdin.flush()
elif cmd == 'stop':
- proc.send_signal(signal.SIGINT)
+ kill(proc, signal.SIGINT)
elif cmd == 'done':
break
except:
@@ -155,7 +167,7 @@ def _interpreter(control, callback):
data = proc.stdout.read()
if data:
if len(data) > 20000:
- proc.kill()
+ kill(proc, signal.SIGKILL)
proc = None
callback('Child killed for talking too much.\n')
else:
@@ -169,7 +181,7 @@ def _interpreter(control, callback):
elif retcode == -31: # killed by seccomp
callback('Child killed due to sandbox misbehavior.\n')
else:
- callback('Child exited with status "{}".\n'.format(retcode))
+ callback('Child exited with status {}.\n'.format(retcode))
proc = None
# TODO we should select() on control and proc.stdout instead of polling
@@ -177,7 +189,7 @@ def _interpreter(control, callback):
# We are done, kill the child.
if proc is not None:
- proc.kill()
+ kill(proc, signal.SIGKILL)
# Execute [code] and evaluate [expr]. Input is given by the string [stdin].
# Return result of evaluation, the contents of stdout and stderr, and the