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