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
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
cmd_line = [trampoline, str(uid), req.config['paths']['jails']['mounts'],
143
req.config['paths']['jails']['src'],
144
req.config['paths']['jails']['template'],
145
jail_dir, working_dir, interpreter, script_path]
146
# Popen doesn't like unicode strings. It hateses them.
147
cmd_line = [(s.encode('utf-8') if isinstance(s, unicode) else s)
149
pid = subprocess.Popen(cmd_line,
161
pid = subprocess.Popen(
162
[trampoline, str(uid), jail_dir, working_dir, interpreter,
150
164
stdin=f, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
349
363
""" % (warning, text))
365
location_cgi_python = os.path.join(ivle.conf.lib_path, "trampoline")
351
367
# Mapping of interpreter names (as given in conf/app/server.py) to
352
368
# interpreter functions.
354
370
interpreter_objects = {
356
: functools.partial(execute_cgi, "/usr/bin/python"),
372
: functools.partial(execute_cgi, "/usr/bin/python",
373
location_cgi_python),
358
: functools.partial(execute_cgi, None),
375
: functools.partial(execute_cgi, None,
376
location_cgi_python),
359
377
# Should also have:
361
379
# python-server-page
364
def fixup_environ(req, script_path):
382
def fixup_environ(req):
365
383
"""Assuming os.environ has been written with the CGI variables from
366
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'] = ''
390
417
# CGI specifies that REMOTE_HOST SHOULD be set, and MAY just be set to
391
418
# REMOTE_ADDR. Since Apache does not appear to set this, set it to
393
420
if 'REMOTE_HOST' not in env and 'REMOTE_ADDR' in env:
394
421
env['REMOTE_HOST'] = env['REMOTE_ADDR']
396
env['PATH_INFO'] = ''
397
del env['PATH_TRANSLATED']
399
normuri = os.path.normpath(req.uri)
400
env['SCRIPT_NAME'] = normuri
402
423
# SCRIPT_NAME is the path to the script WITHOUT PATH_INFO.
403
# We don't care about these if the script is null (ie. noop).
404
# XXX: We check for /home because we don't want to interfere with
405
# CGIRequest, which fileservice still uses.
406
if script_path and script_path.startswith('/home'):
407
normscript = os.path.normpath(script_path)
409
uri_into_jail = studpath.to_home_path(os.path.normpath(req.path))
411
# PATH_INFO is wrong because the script doesn't physically exist.
412
env['PATH_INFO'] = uri_into_jail[len(normscript):]
413
if len(env['PATH_INFO']) > 0:
414
env['SCRIPT_NAME'] = normuri[:-len(env['PATH_INFO'])]
424
script_name = req.uri
425
env['SCRIPT_NAME'] = script_name
416
427
# SERVER_SOFTWARE is actually not Apache but IVLE, since we are
417
428
# custom-making the CGI request.
418
env['SERVER_SOFTWARE'] = "IVLE/" + ivle.__version__
429
env['SERVER_SOFTWARE'] = "IVLE/" + str(ivle.conf.ivle_version)
420
431
# Additional environment variables
421
username = split_path(req.path)[0]
432
username = studpath.url_to_jailpaths(req.path)[0]
422
433
env['HOME'] = os.path.join('/home', username)
424
class ExecutionError(Exception):
427
def execute_raw(config, user, jail_dir, working_dir, binary, args):
428
'''Execute a binary in a user's jail, returning the raw output.
430
The binary is executed in the given working directory with the given
431
args. A tuple of (stdout, stderr) is returned.
434
tramp = os.path.join(config['paths']['lib'], 'trampoline')
435
tramp_dir = os.path.split(tramp)[0]
437
# Fire up trampoline. Vroom, vroom.
438
cmd_line = [tramp, str(user.unixid), config['paths']['jails']['mounts'],
439
config['paths']['jails']['src'],
440
config['paths']['jails']['template'],
441
jail_dir, working_dir, binary] + args
442
# Popen doesn't like unicode strings. It hateses them.
443
cmd_line = [(s.encode('utf-8') if isinstance(s, unicode) else s)
445
proc = subprocess.Popen(cmd_line,
446
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
447
stderr=subprocess.PIPE, cwd=tramp_dir, close_fds=True)
449
(stdout, stderr) = proc.communicate()
450
exitcode = proc.returncode
453
raise ExecutionError('subprocess ended with code %d, stderr %s' %
454
(exitcode, proc.stderr.read()))
455
return (stdout, stderr)