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

« back to all changes in this revision

Viewing changes to ivle/interpret.py

  • Committer: matt.giuca
  • Date: 2009-01-18 23:03:54 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:1166
setup.install: Merged change from Storm branch (should have been committed
    directly to trunk). Small bugfix.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
# Runs a student script in a safe execution environment.
23
23
 
24
 
import ivle
25
24
from ivle import studpath
26
 
from ivle.util import IVLEError, IVLEJailError, split_path
 
25
from ivle import db
 
26
from ivle.util import IVLEError, IVLEJailError
 
27
import ivle.conf
27
28
 
28
29
import functools
29
30
 
38
39
 
39
40
CGI_BLOCK_SIZE = 65535
40
41
 
 
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
 
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
45
64
    to the individual interpreters to create the jail.
46
65
 
47
66
    req: An IVLE request object.
48
 
    owner: The user who owns the file being served.
 
67
    owner: Username of the user who owns the file being served.
49
68
    jail_dir: Absolute path to the user's jail.
50
69
    filename: Absolute filename within the user's jail.
51
70
    interpreter: A function object to call.
60
79
        filename_abs = os.path.join(os.sep, filename)
61
80
        filename_rel = filename
62
81
 
 
82
    # Get the UID of the owner of the file
63
83
    # (Note: files are executed by their owners, not the logged in user.
64
84
    # This ensures users are responsible for their own programs and also
65
85
    # allows them to be executed by the public).
 
86
    uid = get_uid(owner)
66
87
 
67
88
    # Split up req.path again, this time with respect to the jail
68
89
    (working_dir, _) = os.path.split(filename_abs)
73
94
    # (Note that paths "relative" to the jail actually begin with a '/' as
74
95
    # they are absolute in the jailspace)
75
96
 
76
 
    return interpreter(owner.unixid, jail_dir, working_dir, filename_abs, req,
 
97
    return interpreter(uid, jail_dir, working_dir, filename_abs, req,
77
98
                       gentle)
78
99
 
79
100
class CGIFlags:
88
109
        self.linebuf = ""
89
110
        self.headers = {}       # Header names : values
90
111
 
91
 
def execute_cgi(interpreter, uid, jail_dir, working_dir, script_path,
92
 
                req, gentle):
 
112
def execute_cgi(interpreter, trampoline, uid, jail_dir, working_dir,
 
113
                script_path, req, gentle):
93
114
    """
94
115
    trampoline: Full path on the local system to the CGI wrapper program
95
116
        being executed.
105
126
    its environment.
106
127
    """
107
128
 
108
 
    trampoline = os.path.join(req.config['paths']['lib'], 'trampoline')
109
 
 
110
129
    # Support no-op trampoline runs.
111
130
    if interpreter is None:
112
131
        interpreter = '/bin/true'
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)
 
158
    fixup_environ(req)
140
159
 
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,
 
163
        script_path],
147
164
        stdin=f, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
148
165
        cwd=tramp_dir)
149
166
 
345
362
    <pre>
346
363
""" % (warning, text))
347
364
 
 
365
location_cgi_python = os.path.join(ivle.conf.lib_path, "trampoline")
 
366
 
348
367
# Mapping of interpreter names (as given in conf/app/server.py) to
349
368
# interpreter functions.
350
369
 
351
370
interpreter_objects = {
352
371
    'cgi-python'
353
 
        : functools.partial(execute_cgi, "/usr/bin/python"),
 
372
        : functools.partial(execute_cgi, "/usr/bin/python",
 
373
            location_cgi_python),
354
374
    'noop'
355
 
        : functools.partial(execute_cgi, None),
 
375
        : functools.partial(execute_cgi, None,
 
376
            location_cgi_python),
356
377
    # Should also have:
357
378
    # cgi-generic
358
379
    # python-server-page
359
380
}
360
381
 
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.
364
385
 
384
405
        del env['PATH']
385
406
    except: pass
386
407
 
 
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
 
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
389
419
    # REMOTE_ADDR.
390
420
    if 'REMOTE_HOST' not in env and 'REMOTE_ADDR' in env:
391
421
        env['REMOTE_HOST'] = env['REMOTE_ADDR']
392
422
 
393
 
    env['PATH_INFO'] = ''
394
 
    del env['PATH_TRANSLATED']
395
 
 
396
 
    normuri = os.path.normpath(req.uri)
397
 
    env['SCRIPT_NAME'] = normuri
398
 
 
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)
405
 
 
406
 
        uri_into_jail = studpath.to_home_path(os.path.normpath(req.path))
407
 
 
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
412
426
 
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)
416
430
 
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)
420
 
 
421
 
class ExecutionError(Exception):
422
 
    pass
423
 
 
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.
426
 
 
427
 
    The binary is executed in the given working directory with the given
428
 
    args. A tuple of (stdout, stderr) is returned.
429
 
    '''
430
 
 
431
 
    tramp = os.path.join(config['paths']['lib'], 'trampoline')
432
 
    tramp_dir = os.path.split(tramp)[0]
433
 
 
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)
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)