~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to ivle/interpret.py

  • Committer: William Grant
  • Date: 2009-04-28 04:50:39 UTC
  • Revision ID: grantw@unimelb.edu.au-20090428045039-ibb7gwtjrhe9osq3
Populate req.config in a cleaner manner.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
# Runs a student script in a safe execution environment.
23
23
 
24
24
from ivle import studpath
25
 
from ivle import db
26
25
from ivle.util import IVLEError, IVLEJailError
27
26
import ivle.conf
28
27
 
39
38
 
40
39
CGI_BLOCK_SIZE = 65535
41
40
 
42
 
uids = {}
43
 
 
44
 
def get_uid(login):
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."""
48
 
    global uids
49
 
    if login in uids:
50
 
        return uids[login]
51
 
 
52
 
    conn = db.DB()
53
 
    res = conn.get_all('login', ['login', 'unixid'])
54
 
    def repack(flds):
55
 
        return (flds['login'], flds['unixid'])
56
 
    uids = dict(map(repack,res))
57
 
 
58
 
    return uids[login]
59
 
 
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
64
45
    to the individual interpreters to create the jail.
65
46
 
66
47
    req: An IVLE request object.
67
 
    owner: Username of the user who owns the file being served.
 
48
    owner: The user who owns the file being served.
68
49
    jail_dir: Absolute path to the user's jail.
69
50
    filename: Absolute filename within the user's jail.
70
51
    interpreter: A function object to call.
79
60
        filename_abs = os.path.join(os.sep, filename)
80
61
        filename_rel = filename
81
62
 
82
 
    # Get the UID of the owner of the file
83
63
    # (Note: files are executed by their owners, not the logged in user.
84
64
    # This ensures users are responsible for their own programs and also
85
65
    # allows them to be executed by the public).
86
 
    uid = get_uid(owner)
87
66
 
88
67
    # Split up req.path again, this time with respect to the jail
89
68
    (working_dir, _) = os.path.split(filename_abs)
94
73
    # (Note that paths "relative" to the jail actually begin with a '/' as
95
74
    # they are absolute in the jailspace)
96
75
 
97
 
    return interpreter(uid, jail_dir, working_dir, filename_abs, req,
 
76
    return interpreter(owner.unixid, jail_dir, working_dir, filename_abs, req,
98
77
                       gentle)
99
78
 
100
79
class CGIFlags:
155
134
        del os.environ[k]
156
135
    for (k,v) in req.get_cgi_environ().items():
157
136
        os.environ[k] = v
158
 
    fixup_environ(req)
 
137
    fixup_environ(req, script_path)
159
138
 
160
139
    # usage: tramp uid jail_dir working_dir script_path
161
140
    pid = subprocess.Popen(
162
 
        [trampoline, str(uid), jail_dir, working_dir, interpreter,
 
141
        [trampoline, str(uid), ivle.conf.jail_base, ivle.conf.jail_src_base,
 
142
         ivle.conf.jail_system, jail_dir, working_dir, interpreter,
163
143
        script_path],
164
144
        stdin=f, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
165
145
        cwd=tramp_dir)
379
359
    # python-server-page
380
360
}
381
361
 
382
 
def fixup_environ(req):
 
362
def fixup_environ(req, script_path):
383
363
    """Assuming os.environ has been written with the CGI variables from
384
364
    apache, make a few changes for security and correctness.
385
365
 
405
385
        del env['PATH']
406
386
    except: pass
407
387
 
408
 
    # Remove SCRIPT_FILENAME. Not part of CGI spec (see SCRIPT_NAME).
409
 
 
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'] = ''
416
 
 
417
388
    # CGI specifies that REMOTE_HOST SHOULD be set, and MAY just be set to
418
389
    # REMOTE_ADDR. Since Apache does not appear to set this, set it to
419
390
    # REMOTE_ADDR.
420
391
    if 'REMOTE_HOST' not in env and 'REMOTE_ADDR' in env:
421
392
        env['REMOTE_HOST'] = env['REMOTE_ADDR']
422
393
 
 
394
    env['PATH_INFO'] = ''
 
395
    del env['PATH_TRANSLATED']
 
396
 
 
397
    normuri = os.path.normpath(req.uri)
 
398
    env['SCRIPT_NAME'] = normuri
 
399
 
423
400
    # SCRIPT_NAME is the path to the script WITHOUT PATH_INFO.
424
 
    script_name = req.uri
425
 
    env['SCRIPT_NAME'] = script_name
 
401
    # We don't care about these if the script is null (ie. noop).
 
402
    # XXX: We check for /home because we don't want to interfere with
 
403
    # CGIRequest, which fileservice still uses.
 
404
    if script_path and script_path.startswith('/home'):
 
405
        normscript = os.path.normpath(script_path)
 
406
 
 
407
        uri_into_jail = studpath.url_to_jailpaths(os.path.normpath(req.path))[2]
 
408
 
 
409
        # PATH_INFO is wrong because the script doesn't physically exist.
 
410
        env['PATH_INFO'] = uri_into_jail[len(normscript):]
 
411
        if len(env['PATH_INFO']) > 0:
 
412
            env['SCRIPT_NAME'] = normuri[:-len(env['PATH_INFO'])]
426
413
 
427
414
    # SERVER_SOFTWARE is actually not Apache but IVLE, since we are
428
415
    # custom-making the CGI request.
431
418
    # Additional environment variables
432
419
    username = studpath.url_to_jailpaths(req.path)[0]
433
420
    env['HOME'] = os.path.join('/home', username)
 
421
 
 
422
class ExecutionError(Exception):
 
423
    pass
 
424
 
 
425
def execute_raw(user, jail_dir, working_dir, binary, args):
 
426
    '''Execute a binary in a user's jail, returning the raw output.
 
427
 
 
428
    The binary is executed in the given working directory with the given
 
429
    args. A tuple of (stdout, stderr) is returned.
 
430
    '''
 
431
 
 
432
    tramp = location_cgi_python
 
433
    tramp_dir = os.path.split(location_cgi_python)[0]
 
434
 
 
435
    # Fire up trampoline. Vroom, vroom.
 
436
    proc = subprocess.Popen(
 
437
        [tramp, str(user.unixid), ivle.conf.jail_base,
 
438
         ivle.conf.jail_src_base, ivle.conf.jail_system, jail_dir,
 
439
         working_dir, binary] + args,
 
440
        stdin=subprocess.PIPE, stdout=subprocess.PIPE,
 
441
        stderr=subprocess.PIPE, cwd=tramp_dir, close_fds=True)
 
442
 
 
443
    (stdout, stderr) = proc.communicate()
 
444
    exitcode = proc.returncode
 
445
 
 
446
    if exitcode != 0:
 
447
        raise ExecutionError('subprocess ended with code %d, stderr %s' %
 
448
                             (exitcode, proc.stderr.read()))
 
449
    return (stdout, stderr)