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