/Doc/library/rexec.rst
http://unladen-swallow.googlecode.com/ · ReStructuredText · 289 lines · 194 code · 95 blank · 0 comment · 0 complexity · 504d0fe3f6013f835ecc1447f7d08101 MD5 · raw file
- :mod:`rexec` --- Restricted execution framework
- ===============================================
- .. module:: rexec
- :synopsis: Basic restricted execution framework.
- :deprecated:
- .. deprecated:: 2.6
- The :mod:`rexec` module has been removed in Python 3.0.
- .. versionchanged:: 2.3
- Disabled module.
- .. warning::
- The documentation has been left in place to help in reading old code that uses
- the module.
- This module contains the :class:`RExec` class, which supports :meth:`r_eval`,
- :meth:`r_execfile`, :meth:`r_exec`, and :meth:`r_import` methods, which are
- restricted versions of the standard Python functions :meth:`eval`,
- :meth:`execfile` and the :keyword:`exec` and :keyword:`import` statements. Code
- executed in this restricted environment will only have access to modules and
- functions that are deemed safe; you can subclass :class:`RExec` to add or remove
- capabilities as desired.
- .. warning::
- While the :mod:`rexec` module is designed to perform as described below, it does
- have a few known vulnerabilities which could be exploited by carefully written
- code. Thus it should not be relied upon in situations requiring "production
- ready" security. In such situations, execution via sub-processes or very
- careful "cleansing" of both code and data to be processed may be necessary.
- Alternatively, help in patching known :mod:`rexec` vulnerabilities would be
- welcomed.
- .. note::
- The :class:`RExec` class can prevent code from performing unsafe operations like
- reading or writing disk files, or using TCP/IP sockets. However, it does not
- protect against code using extremely large amounts of memory or processor time.
- .. class:: RExec([hooks[, verbose]])
- Returns an instance of the :class:`RExec` class.
- *hooks* is an instance of the :class:`RHooks` class or a subclass of it. If it
- is omitted or ``None``, the default :class:`RHooks` class is instantiated.
- Whenever the :mod:`rexec` module searches for a module (even a built-in one) or
- reads a module's code, it doesn't actually go out to the file system itself.
- Rather, it calls methods of an :class:`RHooks` instance that was passed to or
- created by its constructor. (Actually, the :class:`RExec` object doesn't make
- these calls --- they are made by a module loader object that's part of the
- :class:`RExec` object. This allows another level of flexibility, which can be
- useful when changing the mechanics of :keyword:`import` within the restricted
- environment.)
- By providing an alternate :class:`RHooks` object, we can control the file system
- accesses made to import a module, without changing the actual algorithm that
- controls the order in which those accesses are made. For instance, we could
- substitute an :class:`RHooks` object that passes all filesystem requests to a
- file server elsewhere, via some RPC mechanism such as ILU. Grail's applet
- loader uses this to support importing applets from a URL for a directory.
- If *verbose* is true, additional debugging output may be sent to standard
- output.
- It is important to be aware that code running in a restricted environment can
- still call the :func:`sys.exit` function. To disallow restricted code from
- exiting the interpreter, always protect calls that cause restricted code to run
- with a :keyword:`try`/:keyword:`except` statement that catches the
- :exc:`SystemExit` exception. Removing the :func:`sys.exit` function from the
- restricted environment is not sufficient --- the restricted code could still use
- ``raise SystemExit``. Removing :exc:`SystemExit` is not a reasonable option;
- some library code makes use of this and would break were it not available.
- .. seealso::
- `Grail Home Page <http://grail.sourceforge.net/>`_
- Grail is a Web browser written entirely in Python. It uses the :mod:`rexec`
- module as a foundation for supporting Python applets, and can be used as an
- example usage of this module.
- .. _rexec-objects:
- RExec Objects
- -------------
- :class:`RExec` instances support the following methods:
- .. method:: RExec.r_eval(code)
- *code* must either be a string containing a Python expression, or a compiled
- code object, which will be evaluated in the restricted environment's
- :mod:`__main__` module. The value of the expression or code object will be
- returned.
- .. method:: RExec.r_exec(code)
- *code* must either be a string containing one or more lines of Python code, or a
- compiled code object, which will be executed in the restricted environment's
- :mod:`__main__` module.
- .. method:: RExec.r_execfile(filename)
- Execute the Python code contained in the file *filename* in the restricted
- environment's :mod:`__main__` module.
- Methods whose names begin with ``s_`` are similar to the functions beginning
- with ``r_``, but the code will be granted access to restricted versions of the
- standard I/O streams ``sys.stdin``, ``sys.stderr``, and ``sys.stdout``.
- .. method:: RExec.s_eval(code)
- *code* must be a string containing a Python expression, which will be evaluated
- in the restricted environment.
- .. method:: RExec.s_exec(code)
- *code* must be a string containing one or more lines of Python code, which will
- be executed in the restricted environment.
- .. method:: RExec.s_execfile(code)
- Execute the Python code contained in the file *filename* in the restricted
- environment.
- :class:`RExec` objects must also support various methods which will be
- implicitly called by code executing in the restricted environment. Overriding
- these methods in a subclass is used to change the policies enforced by a
- restricted environment.
- .. method:: RExec.r_import(modulename[, globals[, locals[, fromlist]]])
- Import the module *modulename*, raising an :exc:`ImportError` exception if the
- module is considered unsafe.
- .. method:: RExec.r_open(filename[, mode[, bufsize]])
- Method called when :func:`open` is called in the restricted environment. The
- arguments are identical to those of :func:`open`, and a file object (or a class
- instance compatible with file objects) should be returned. :class:`RExec`'s
- default behaviour is allow opening any file for reading, but forbidding any
- attempt to write a file. See the example below for an implementation of a less
- restrictive :meth:`r_open`.
- .. method:: RExec.r_reload(module)
- Reload the module object *module*, re-parsing and re-initializing it.
- .. method:: RExec.r_unload(module)
- Unload the module object *module* (remove it from the restricted environment's
- ``sys.modules`` dictionary).
- And their equivalents with access to restricted standard I/O streams:
- .. method:: RExec.s_import(modulename[, globals[, locals[, fromlist]]])
- Import the module *modulename*, raising an :exc:`ImportError` exception if the
- module is considered unsafe.
- .. method:: RExec.s_reload(module)
- Reload the module object *module*, re-parsing and re-initializing it.
- .. method:: RExec.s_unload(module)
- Unload the module object *module*.
- .. XXX what are the semantics of this?
- .. _rexec-extension:
- Defining restricted environments
- --------------------------------
- The :class:`RExec` class has the following class attributes, which are used by
- the :meth:`__init__` method. Changing them on an existing instance won't have
- any effect; instead, create a subclass of :class:`RExec` and assign them new
- values in the class definition. Instances of the new class will then use those
- new values. All these attributes are tuples of strings.
- .. attribute:: RExec.nok_builtin_names
- Contains the names of built-in functions which will *not* be available to
- programs running in the restricted environment. The value for :class:`RExec` is
- ``('open', 'reload', '__import__')``. (This gives the exceptions, because by far
- the majority of built-in functions are harmless. A subclass that wants to
- override this variable should probably start with the value from the base class
- and concatenate additional forbidden functions --- when new dangerous built-in
- functions are added to Python, they will also be added to this module.)
- .. attribute:: RExec.ok_builtin_modules
- Contains the names of built-in modules which can be safely imported. The value
- for :class:`RExec` is ``('audioop', 'array', 'binascii', 'cmath', 'errno',
- 'imageop', 'marshal', 'math', 'md5', 'operator', 'parser', 'regex', 'select',
- 'sha', '_sre', 'strop', 'struct', 'time')``. A similar remark about overriding
- this variable applies --- use the value from the base class as a starting point.
- .. attribute:: RExec.ok_path
- Contains the directories which will be searched when an :keyword:`import` is
- performed in the restricted environment. The value for :class:`RExec` is the
- same as ``sys.path`` (at the time the module is loaded) for unrestricted code.
- .. attribute:: RExec.ok_posix_names
- Contains the names of the functions in the :mod:`os` module which will be
- available to programs running in the restricted environment. The value for
- :class:`RExec` is ``('error', 'fstat', 'listdir', 'lstat', 'readlink', 'stat',
- 'times', 'uname', 'getpid', 'getppid', 'getcwd', 'getuid', 'getgid', 'geteuid',
- 'getegid')``.
- .. Should this be called ok_os_names?
- .. attribute:: RExec.ok_sys_names
- Contains the names of the functions and variables in the :mod:`sys` module which
- will be available to programs running in the restricted environment. The value
- for :class:`RExec` is ``('ps1', 'ps2', 'copyright', 'version', 'platform',
- 'exit', 'maxint')``.
- .. attribute:: RExec.ok_file_types
- Contains the file types from which modules are allowed to be loaded. Each file
- type is an integer constant defined in the :mod:`imp` module. The meaningful
- values are :const:`PY_SOURCE`, :const:`PY_COMPILED`, and :const:`C_EXTENSION`.
- The value for :class:`RExec` is ``(C_EXTENSION, PY_SOURCE)``. Adding
- :const:`PY_COMPILED` in subclasses is not recommended; an attacker could exit
- the restricted execution mode by putting a forged byte-compiled file
- (:file:`.pyc`) anywhere in your file system, for example by writing it to
- :file:`/tmp` or uploading it to the :file:`/incoming` directory of your public
- FTP server.
- An example
- ----------
- Let us say that we want a slightly more relaxed policy than the standard
- :class:`RExec` class. For example, if we're willing to allow files in
- :file:`/tmp` to be written, we can subclass the :class:`RExec` class::
- class TmpWriterRExec(rexec.RExec):
- def r_open(self, file, mode='r', buf=-1):
- if mode in ('r', 'rb'):
- pass
- elif mode in ('w', 'wb', 'a', 'ab'):
- # check filename : must begin with /tmp/
- if file[:5]!='/tmp/':
- raise IOError, "can't write outside /tmp"
- elif (string.find(file, '/../') >= 0 or
- file[:3] == '../' or file[-3:] == '/..'):
- raise IOError, "'..' in filename forbidden"
- else: raise IOError, "Illegal open() mode"
- return open(file, mode, buf)
- Notice that the above code will occasionally forbid a perfectly valid filename;
- for example, code in the restricted environment won't be able to open a file
- called :file:`/tmp/foo/../bar`. To fix this, the :meth:`r_open` method would
- have to simplify the filename to :file:`/tmp/bar`, which would require splitting
- apart the filename and performing various operations on it. In cases where
- security is at stake, it may be preferable to write simple code which is
- sometimes overly restrictive, instead of more general code that is also more
- complex and may harbor a subtle security hole.