blob: 5747d82c4a77263b7bd4fa4f96a9a04a1c4c6612 [file] [log] [blame]
Georg Brandl8ec7f652007-08-15 14:28:01 +00001
2:mod:`rexec` --- Restricted execution framework
3===============================================
4
5.. module:: rexec
6 :synopsis: Basic restricted execution framework.
7
8
9.. versionchanged:: 2.3
10 Disabled module.
11
12.. warning::
13
14 The documentation has been left in place to help in reading old code that uses
15 the module.
16
17This module contains the :class:`RExec` class, which supports :meth:`r_eval`,
18:meth:`r_execfile`, :meth:`r_exec`, and :meth:`r_import` methods, which are
19restricted versions of the standard Python functions :meth:`eval`,
20:meth:`execfile` and the :keyword:`exec` and :keyword:`import` statements. Code
21executed in this restricted environment will only have access to modules and
22functions that are deemed safe; you can subclass :class:`RExec` to add or remove
23capabilities as desired.
24
25.. warning::
26
27 While the :mod:`rexec` module is designed to perform as described below, it does
28 have a few known vulnerabilities which could be exploited by carefully written
29 code. Thus it should not be relied upon in situations requiring "production
30 ready" security. In such situations, execution via sub-processes or very
31 careful "cleansing" of both code and data to be processed may be necessary.
32 Alternatively, help in patching known :mod:`rexec` vulnerabilities would be
33 welcomed.
34
35.. note::
36
37 The :class:`RExec` class can prevent code from performing unsafe operations like
38 reading or writing disk files, or using TCP/IP sockets. However, it does not
39 protect against code using extremely large amounts of memory or processor time.
40
41
42.. class:: RExec([hooks[, verbose]])
43
44 Returns an instance of the :class:`RExec` class.
45
46 *hooks* is an instance of the :class:`RHooks` class or a subclass of it. If it
47 is omitted or ``None``, the default :class:`RHooks` class is instantiated.
48 Whenever the :mod:`rexec` module searches for a module (even a built-in one) or
49 reads a module's code, it doesn't actually go out to the file system itself.
50 Rather, it calls methods of an :class:`RHooks` instance that was passed to or
51 created by its constructor. (Actually, the :class:`RExec` object doesn't make
52 these calls --- they are made by a module loader object that's part of the
53 :class:`RExec` object. This allows another level of flexibility, which can be
54 useful when changing the mechanics of :keyword:`import` within the restricted
55 environment.)
56
57 By providing an alternate :class:`RHooks` object, we can control the file system
58 accesses made to import a module, without changing the actual algorithm that
59 controls the order in which those accesses are made. For instance, we could
60 substitute an :class:`RHooks` object that passes all filesystem requests to a
61 file server elsewhere, via some RPC mechanism such as ILU. Grail's applet
62 loader uses this to support importing applets from a URL for a directory.
63
64 If *verbose* is true, additional debugging output may be sent to standard
65 output.
66
67It is important to be aware that code running in a restricted environment can
68still call the :func:`sys.exit` function. To disallow restricted code from
69exiting the interpreter, always protect calls that cause restricted code to run
70with a :keyword:`try`/:keyword:`except` statement that catches the
71:exc:`SystemExit` exception. Removing the :func:`sys.exit` function from the
72restricted environment is not sufficient --- the restricted code could still use
73``raise SystemExit``. Removing :exc:`SystemExit` is not a reasonable option;
74some library code makes use of this and would break were it not available.
75
76
77.. seealso::
78
79 `Grail Home Page <http://grail.sourceforge.net/>`_
80 Grail is a Web browser written entirely in Python. It uses the :mod:`rexec`
81 module as a foundation for supporting Python applets, and can be used as an
82 example usage of this module.
83
84
85.. _rexec-objects:
86
87RExec Objects
88-------------
89
90:class:`RExec` instances support the following methods:
91
92
93.. method:: RExec.r_eval(code)
94
95 *code* must either be a string containing a Python expression, or a compiled
96 code object, which will be evaluated in the restricted environment's
97 :mod:`__main__` module. The value of the expression or code object will be
98 returned.
99
100
101.. method:: RExec.r_exec(code)
102
103 *code* must either be a string containing one or more lines of Python code, or a
104 compiled code object, which will be executed in the restricted environment's
105 :mod:`__main__` module.
106
107
108.. method:: RExec.r_execfile(filename)
109
110 Execute the Python code contained in the file *filename* in the restricted
111 environment's :mod:`__main__` module.
112
113Methods whose names begin with ``s_`` are similar to the functions beginning
114with ``r_``, but the code will be granted access to restricted versions of the
115standard I/O streams ``sys.stdin``, ``sys.stderr``, and ``sys.stdout``.
116
117
118.. method:: RExec.s_eval(code)
119
120 *code* must be a string containing a Python expression, which will be evaluated
121 in the restricted environment.
122
123
124.. method:: RExec.s_exec(code)
125
126 *code* must be a string containing one or more lines of Python code, which will
127 be executed in the restricted environment.
128
129
130.. method:: RExec.s_execfile(code)
131
132 Execute the Python code contained in the file *filename* in the restricted
133 environment.
134
135:class:`RExec` objects must also support various methods which will be
136implicitly called by code executing in the restricted environment. Overriding
137these methods in a subclass is used to change the policies enforced by a
138restricted environment.
139
140
141.. method:: RExec.r_import(modulename[, globals[, locals[, fromlist]]])
142
143 Import the module *modulename*, raising an :exc:`ImportError` exception if the
144 module is considered unsafe.
145
146
147.. method:: RExec.r_open(filename[, mode[, bufsize]])
148
149 Method called when :func:`open` is called in the restricted environment. The
150 arguments are identical to those of :func:`open`, and a file object (or a class
151 instance compatible with file objects) should be returned. :class:`RExec`'s
152 default behaviour is allow opening any file for reading, but forbidding any
153 attempt to write a file. See the example below for an implementation of a less
154 restrictive :meth:`r_open`.
155
156
157.. method:: RExec.r_reload(module)
158
159 Reload the module object *module*, re-parsing and re-initializing it.
160
161
162.. method:: RExec.r_unload(module)
163
164 Unload the module object *module* (remove it from the restricted environment's
165 ``sys.modules`` dictionary).
166
167And their equivalents with access to restricted standard I/O streams:
168
169
170.. method:: RExec.s_import(modulename[, globals[, locals[, fromlist]]])
171
172 Import the module *modulename*, raising an :exc:`ImportError` exception if the
173 module is considered unsafe.
174
175
176.. method:: RExec.s_reload(module)
177
178 Reload the module object *module*, re-parsing and re-initializing it.
179
180
181.. method:: RExec.s_unload(module)
182
183 Unload the module object *module*.
184
185 .. % XXX what are the semantics of this?
186
187
188.. _rexec-extension:
189
190Defining restricted environments
191--------------------------------
192
193The :class:`RExec` class has the following class attributes, which are used by
194the :meth:`__init__` method. Changing them on an existing instance won't have
195any effect; instead, create a subclass of :class:`RExec` and assign them new
196values in the class definition. Instances of the new class will then use those
197new values. All these attributes are tuples of strings.
198
199
200.. attribute:: RExec.nok_builtin_names
201
202 Contains the names of built-in functions which will *not* be available to
203 programs running in the restricted environment. The value for :class:`RExec` is
204 ``('open', 'reload', '__import__')``. (This gives the exceptions, because by far
205 the majority of built-in functions are harmless. A subclass that wants to
206 override this variable should probably start with the value from the base class
207 and concatenate additional forbidden functions --- when new dangerous built-in
208 functions are added to Python, they will also be added to this module.)
209
210
211.. attribute:: RExec.ok_builtin_modules
212
213 Contains the names of built-in modules which can be safely imported. The value
214 for :class:`RExec` is ``('audioop', 'array', 'binascii', 'cmath', 'errno',
215 'imageop', 'marshal', 'math', 'md5', 'operator', 'parser', 'regex', 'select',
216 'sha', '_sre', 'strop', 'struct', 'time')``. A similar remark about overriding
217 this variable applies --- use the value from the base class as a starting point.
218
219
220.. attribute:: RExec.ok_path
221
222 Contains the directories which will be searched when an :keyword:`import` is
223 performed in the restricted environment. The value for :class:`RExec` is the
224 same as ``sys.path`` (at the time the module is loaded) for unrestricted code.
225
226
227.. attribute:: RExec.ok_posix_names
228
229 Contains the names of the functions in the :mod:`os` module which will be
230 available to programs running in the restricted environment. The value for
231 :class:`RExec` is ``('error', 'fstat', 'listdir', 'lstat', 'readlink', 'stat',
232 'times', 'uname', 'getpid', 'getppid', 'getcwd', 'getuid', 'getgid', 'geteuid',
233 'getegid')``.
234
235 .. % Should this be called ok_os_names?
236
237
238.. attribute:: RExec.ok_sys_names
239
240 Contains the names of the functions and variables in the :mod:`sys` module which
241 will be available to programs running in the restricted environment. The value
242 for :class:`RExec` is ``('ps1', 'ps2', 'copyright', 'version', 'platform',
243 'exit', 'maxint')``.
244
245
246.. attribute:: RExec.ok_file_types
247
248 Contains the file types from which modules are allowed to be loaded. Each file
249 type is an integer constant defined in the :mod:`imp` module. The meaningful
250 values are :const:`PY_SOURCE`, :const:`PY_COMPILED`, and :const:`C_EXTENSION`.
251 The value for :class:`RExec` is ``(C_EXTENSION, PY_SOURCE)``. Adding
252 :const:`PY_COMPILED` in subclasses is not recommended; an attacker could exit
253 the restricted execution mode by putting a forged byte-compiled file
254 (:file:`.pyc`) anywhere in your file system, for example by writing it to
255 :file:`/tmp` or uploading it to the :file:`/incoming` directory of your public
256 FTP server.
257
258
259An example
260----------
261
262Let us say that we want a slightly more relaxed policy than the standard
263:class:`RExec` class. For example, if we're willing to allow files in
264:file:`/tmp` to be written, we can subclass the :class:`RExec` class::
265
266 class TmpWriterRExec(rexec.RExec):
267 def r_open(self, file, mode='r', buf=-1):
268 if mode in ('r', 'rb'):
269 pass
270 elif mode in ('w', 'wb', 'a', 'ab'):
271 # check filename : must begin with /tmp/
272 if file[:5]!='/tmp/':
273 raise IOError, "can't write outside /tmp"
274 elif (string.find(file, '/../') >= 0 or
275 file[:3] == '../' or file[-3:] == '/..'):
276 raise IOError, "'..' in filename forbidden"
277 else: raise IOError, "Illegal open() mode"
278 return open(file, mode, buf)
279
280Notice that the above code will occasionally forbid a perfectly valid filename;
281for example, code in the restricted environment won't be able to open a file
282called :file:`/tmp/foo/../bar`. To fix this, the :meth:`r_open` method would
283have to simplify the filename to :file:`/tmp/bar`, which would require splitting
284apart the filename and performing various operations on it. In cases where
285security is at stake, it may be preferable to write simple code which is
286sometimes overly restrictive, instead of more general code that is also more
287complex and may harbor a subtle security hole.
288
289.. %
290