40
39
CGI_BLOCK_SIZE = 65535
45
"""Get the unix uid corresponding to the given login name.
46
If it is not in the dictionary of uids, then consult the
47
database and retrieve an update of the user table."""
53
res = conn.get_all('login', ['login', 'unixid'])
55
return (flds['login'], flds['unixid'])
56
uids = dict(map(repack,res))
60
41
def interpret_file(req, owner, jail_dir, filename, interpreter, gentle=True):
61
42
"""Serves a file by interpreting it using one of IVLE's builtin
62
43
interpreters. All interpreters are intended to run in the user's jail. The
110
89
self.headers = {} # Header names : values
112
def execute_cgi(interpreter, trampoline, uid, jail_dir, working_dir,
113
script_path, req, gentle):
91
def execute_cgi(interpreter, uid, jail_dir, working_dir, script_path,
115
94
trampoline: Full path on the local system to the CGI wrapper program
155
136
del os.environ[k]
156
137
for (k,v) in req.get_cgi_environ().items():
157
138
os.environ[k] = v
139
fixup_environ(req, script_path)
160
141
# usage: tramp uid jail_dir working_dir script_path
161
142
pid = subprocess.Popen(
162
[trampoline, str(uid), jail_dir, working_dir, interpreter,
143
[trampoline, str(uid), req.config['paths']['jails']['mounts'],
144
req.config['paths']['jails']['src'],
145
req.config['paths']['jails']['template'],
146
jail_dir, working_dir, interpreter, script_path],
164
147
stdin=f, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
363
346
""" % (warning, text))
365
location_cgi_python = os.path.join(ivle.conf.lib_path, "trampoline")
367
348
# Mapping of interpreter names (as given in conf/app/server.py) to
368
349
# interpreter functions.
370
351
interpreter_objects = {
372
: functools.partial(execute_cgi, "/usr/bin/python",
373
location_cgi_python),
353
: functools.partial(execute_cgi, "/usr/bin/python"),
375
: functools.partial(execute_cgi, None,
376
location_cgi_python),
355
: functools.partial(execute_cgi, None),
377
356
# Should also have:
379
358
# python-server-page
382
def fixup_environ(req):
361
def fixup_environ(req, script_path):
383
362
"""Assuming os.environ has been written with the CGI variables from
384
363
apache, make a few changes for security and correctness.
408
# Remove SCRIPT_FILENAME. Not part of CGI spec (see SCRIPT_NAME).
410
# PATH_INFO is wrong because the script doesn't physically exist.
411
# Apache makes it relative to the "serve" app. It should actually be made
412
# relative to the student's script. intepretservice does that in the jail,
413
# so here we just clear it.
414
env['PATH_INFO'] = ''
415
env['PATH_TRANSLATED'] = ''
417
387
# CGI specifies that REMOTE_HOST SHOULD be set, and MAY just be set to
418
388
# REMOTE_ADDR. Since Apache does not appear to set this, set it to
420
390
if 'REMOTE_HOST' not in env and 'REMOTE_ADDR' in env:
421
391
env['REMOTE_HOST'] = env['REMOTE_ADDR']
393
env['PATH_INFO'] = ''
394
del env['PATH_TRANSLATED']
396
normuri = os.path.normpath(req.uri)
397
env['SCRIPT_NAME'] = normuri
423
399
# SCRIPT_NAME is the path to the script WITHOUT PATH_INFO.
424
script_name = req.uri
425
env['SCRIPT_NAME'] = script_name
400
# We don't care about these if the script is null (ie. noop).
401
# XXX: We check for /home because we don't want to interfere with
402
# CGIRequest, which fileservice still uses.
403
if script_path and script_path.startswith('/home'):
404
normscript = os.path.normpath(script_path)
406
uri_into_jail = studpath.to_home_path(os.path.normpath(req.path))
408
# PATH_INFO is wrong because the script doesn't physically exist.
409
env['PATH_INFO'] = uri_into_jail[len(normscript):]
410
if len(env['PATH_INFO']) > 0:
411
env['SCRIPT_NAME'] = normuri[:-len(env['PATH_INFO'])]
427
413
# SERVER_SOFTWARE is actually not Apache but IVLE, since we are
428
414
# custom-making the CGI request.
429
env['SERVER_SOFTWARE'] = "IVLE/" + str(ivle.conf.ivle_version)
415
env['SERVER_SOFTWARE'] = "IVLE/" + ivle.__version__
431
417
# Additional environment variables
432
username = studpath.url_to_jailpaths(req.path)[0]
418
username = split_path(req.path)[0]
433
419
env['HOME'] = os.path.join('/home', username)
421
class ExecutionError(Exception):
424
def execute_raw(config, user, jail_dir, working_dir, binary, args):
425
'''Execute a binary in a user's jail, returning the raw output.
427
The binary is executed in the given working directory with the given
428
args. A tuple of (stdout, stderr) is returned.
431
tramp = os.path.join(config['paths']['lib'], 'trampoline')
432
tramp_dir = os.path.split(tramp)[0]
434
# Fire up trampoline. Vroom, vroom.
435
proc = subprocess.Popen(
436
[tramp, str(user.unixid), config['paths']['jails']['mounts'],
437
config['paths']['jails']['src'],
438
config['paths']['jails']['template'],
439
jail_dir, working_dir, binary] + args,
440
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
441
stderr=subprocess.PIPE, cwd=tramp_dir, close_fds=True)
443
(stdout, stderr) = proc.communicate()
444
exitcode = proc.returncode
447
raise ExecutionError('subprocess ended with code %d, stderr %s' %
448
(exitcode, proc.stderr.read()))
449
return (stdout, stderr)