Georg Brandl | 8ec7f65 | 2007-08-15 14:28:01 +0000 | [diff] [blame] | 1 | :mod:`rexec` --- Restricted execution framework |
| 2 | =============================================== |
| 3 | |
| 4 | .. module:: rexec |
| 5 | :synopsis: Basic restricted execution framework. |
Brett Cannon | 4c1f881 | 2008-05-10 02:27:04 +0000 | [diff] [blame] | 6 | :deprecated: |
Georg Brandl | c62ef8b | 2009-01-03 20:55:06 +0000 | [diff] [blame] | 7 | |
Brett Cannon | 4c1f881 | 2008-05-10 02:27:04 +0000 | [diff] [blame] | 8 | .. deprecated:: 2.6 |
Ezio Melotti | 510ff54 | 2012-05-03 19:21:40 +0300 | [diff] [blame] | 9 | The :mod:`rexec` module has been removed in Python 3. |
Georg Brandl | 8ec7f65 | 2007-08-15 14:28:01 +0000 | [diff] [blame] | 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 | |
| 19 | This 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 |
| 21 | restricted versions of the standard Python functions :meth:`eval`, |
| 22 | :meth:`execfile` and the :keyword:`exec` and :keyword:`import` statements. Code |
| 23 | executed in this restricted environment will only have access to modules and |
| 24 | functions that are deemed safe; you can subclass :class:`RExec` to add or remove |
| 25 | capabilities 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 | |
| 69 | It is important to be aware that code running in a restricted environment can |
| 70 | still call the :func:`sys.exit` function. To disallow restricted code from |
| 71 | exiting the interpreter, always protect calls that cause restricted code to run |
| 72 | with a :keyword:`try`/:keyword:`except` statement that catches the |
| 73 | :exc:`SystemExit` exception. Removing the :func:`sys.exit` function from the |
| 74 | restricted environment is not sufficient --- the restricted code could still use |
| 75 | ``raise SystemExit``. Removing :exc:`SystemExit` is not a reasonable option; |
| 76 | some 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 | |
| 89 | RExec 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 | |
| 115 | Methods whose names begin with ``s_`` are similar to the functions beginning |
| 116 | with ``r_``, but the code will be granted access to restricted versions of the |
| 117 | standard 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 |
| 138 | implicitly called by code executing in the restricted environment. Overriding |
| 139 | these methods in a subclass is used to change the policies enforced by a |
| 140 | restricted 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 | |
| 169 | And 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 | |
Georg Brandl | b19be57 | 2007-12-29 10:57:00 +0000 | [diff] [blame] | 187 | .. XXX what are the semantics of this? |
Georg Brandl | 8ec7f65 | 2007-08-15 14:28:01 +0000 | [diff] [blame] | 188 | |
| 189 | |
| 190 | .. _rexec-extension: |
| 191 | |
| 192 | Defining restricted environments |
| 193 | -------------------------------- |
| 194 | |
| 195 | The :class:`RExec` class has the following class attributes, which are used by |
| 196 | the :meth:`__init__` method. Changing them on an existing instance won't have |
| 197 | any effect; instead, create a subclass of :class:`RExec` and assign them new |
| 198 | values in the class definition. Instances of the new class will then use those |
| 199 | new 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 | |
Georg Brandl | b19be57 | 2007-12-29 10:57:00 +0000 | [diff] [blame] | 237 | .. Should this be called ok_os_names? |
Georg Brandl | 8ec7f65 | 2007-08-15 14:28:01 +0000 | [diff] [blame] | 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 | |
| 261 | An example |
| 262 | ---------- |
| 263 | |
| 264 | Let 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'): |
Serhiy Storchaka | b712873 | 2013-12-24 11:04:06 +0200 | [diff] [blame] | 273 | # check filename: must begin with /tmp/ |
Georg Brandl | c62ef8b | 2009-01-03 20:55:06 +0000 | [diff] [blame] | 274 | if file[:5]!='/tmp/': |
Georg Brandl | c1edec3 | 2009-06-03 07:25:35 +0000 | [diff] [blame] | 275 | raise IOError("can't write outside /tmp") |
Georg Brandl | 8ec7f65 | 2007-08-15 14:28:01 +0000 | [diff] [blame] | 276 | elif (string.find(file, '/../') >= 0 or |
| 277 | file[:3] == '../' or file[-3:] == '/..'): |
Georg Brandl | c1edec3 | 2009-06-03 07:25:35 +0000 | [diff] [blame] | 278 | raise IOError("'..' in filename forbidden") |
| 279 | else: raise IOError("Illegal open() mode") |
Georg Brandl | 8ec7f65 | 2007-08-15 14:28:01 +0000 | [diff] [blame] | 280 | return open(file, mode, buf) |
| 281 | |
| 282 | Notice that the above code will occasionally forbid a perfectly valid filename; |
| 283 | for example, code in the restricted environment won't be able to open a file |
| 284 | called :file:`/tmp/foo/../bar`. To fix this, the :meth:`r_open` method would |
| 285 | have to simplify the filename to :file:`/tmp/bar`, which would require splitting |
| 286 | apart the filename and performing various operations on it. In cases where |
| 287 | security is at stake, it may be preferable to write simple code which is |
| 288 | sometimes overly restrictive, instead of more general code that is also more |
| 289 | complex and may harbor a subtle security hole. |