39
40
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))
41
60
def interpret_file(req, owner, jail_dir, filename, interpreter, gentle=True):
42
61
"""Serves a file by interpreting it using one of IVLE's builtin
43
62
interpreters. All interpreters are intended to run in the user's jail. The
89
110
self.headers = {} # Header names : values
91
def execute_cgi(interpreter, uid, jail_dir, working_dir, script_path,
112
def execute_cgi(interpreter, trampoline, uid, jail_dir, working_dir,
113
script_path, req, gentle):
94
115
trampoline: Full path on the local system to the CGI wrapper program
136
155
del os.environ[k]
137
156
for (k,v) in req.get_cgi_environ().items():
138
157
os.environ[k] = v
139
fixup_environ(req, script_path)
141
160
# usage: tramp uid jail_dir working_dir script_path
142
161
pid = subprocess.Popen(
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],
162
[trampoline, str(uid), jail_dir, working_dir, interpreter,
147
164
stdin=f, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
346
363
""" % (warning, text))
365
location_cgi_python = os.path.join(ivle.conf.lib_path, "trampoline")
348
367
# Mapping of interpreter names (as given in conf/app/server.py) to
349
368
# interpreter functions.
351
370
interpreter_objects = {
353
: functools.partial(execute_cgi, "/usr/bin/python"),
372
: functools.partial(execute_cgi, "/usr/bin/python",
373
location_cgi_python),
355
: functools.partial(execute_cgi, None),
375
: functools.partial(execute_cgi, None,
376
location_cgi_python),
356
377
# Should also have:
358
379
# python-server-page
361
def fixup_environ(req, script_path):
382
def fixup_environ(req):
362
383
"""Assuming os.environ has been written with the CGI variables from
363
384
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'] = ''
387
417
# CGI specifies that REMOTE_HOST SHOULD be set, and MAY just be set to
388
418
# REMOTE_ADDR. Since Apache does not appear to set this, set it to
390
420
if 'REMOTE_HOST' not in env and 'REMOTE_ADDR' in env:
391
421
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
399
423
# SCRIPT_NAME is the path to the script WITHOUT PATH_INFO.
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'])]
424
script_name = req.uri
425
env['SCRIPT_NAME'] = script_name
413
427
# SERVER_SOFTWARE is actually not Apache but IVLE, since we are
414
428
# custom-making the CGI request.
415
env['SERVER_SOFTWARE'] = "IVLE/" + ivle.__version__
429
env['SERVER_SOFTWARE'] = "IVLE/" + str(ivle.conf.ivle_version)
417
431
# Additional environment variables
418
username = split_path(req.path)[0]
432
username = studpath.url_to_jailpaths(req.path)[0]
419
433
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)