mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 03:22:27 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			246 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			TeX
		
	
	
	
	
	
			
		
		
	
	
			246 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			TeX
		
	
	
	
	
	
\section{\module{rexec} ---
 | 
						|
         Restricted execution framework}
 | 
						|
 | 
						|
\declaremodule{standard}{rexec}
 | 
						|
\modulesynopsis{Basic restricted execution framework.}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
This module contains the \class{RExec} class, which supports
 | 
						|
\method{r_eval()}, \method{r_execfile()}, \method{r_exec()}, and
 | 
						|
\method{r_import()} methods, which are restricted versions of the standard
 | 
						|
Python functions \method{eval()}, \method{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.
 | 
						|
 | 
						|
\emph{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.  
 | 
						|
 | 
						|
\begin{classdesc}{RExec}{\optional{hooks\optional{, verbose}}}
 | 
						|
Returns an instance of the \class{RExec} class.  
 | 
						|
 | 
						|
\var{hooks} is an instance of the \class{RHooks} class or a subclass of it.
 | 
						|
If it is omitted or \code{None}, the default \class{RHooks} class is
 | 
						|
instantiated.
 | 
						|
Whenever the \module{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 \var{verbose} is true, additional debugging output may be sent to
 | 
						|
standard output.
 | 
						|
\end{classdesc}
 | 
						|
 | 
						|
It is important to be aware that code running in a restricted
 | 
						|
environment can still call the \function{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
 | 
						|
\exception{SystemExit} exception.  Removing the \function{sys.exit()}
 | 
						|
function from the restricted environment is not sufficient --- the
 | 
						|
restricted code could still use \code{raise SystemExit}.  Removing
 | 
						|
\exception{SystemExit} is not a reasonable option; some library code
 | 
						|
makes use of this and would break were it not available.
 | 
						|
 | 
						|
 | 
						|
\begin{seealso}
 | 
						|
  \seetitle[http://grail.sourceforge.net/]{Grail Home Page}{Grail is a
 | 
						|
            Web browser written entirely in Python.  It uses the
 | 
						|
            \module{rexec} module as a foundation for supporting
 | 
						|
            Python applets, and can be used as an example usage of
 | 
						|
            this module.}
 | 
						|
\end{seealso}
 | 
						|
 | 
						|
 | 
						|
\subsection{RExec Objects \label{rexec-objects}}
 | 
						|
 | 
						|
\class{RExec} instances support the following methods:
 | 
						|
 | 
						|
\begin{methoddesc}{r_eval}{code}
 | 
						|
\var{code} must either be a string containing a Python expression, or
 | 
						|
a compiled code object, which will be evaluated in the restricted
 | 
						|
environment's \module{__main__} module.  The value of the expression or
 | 
						|
code object will be returned.
 | 
						|
\end{methoddesc}
 | 
						|
 | 
						|
\begin{methoddesc}{r_exec}{code}
 | 
						|
\var{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 \module{__main__} module.
 | 
						|
\end{methoddesc}
 | 
						|
 | 
						|
\begin{methoddesc}{r_execfile}{filename}
 | 
						|
Execute the Python code contained in the file \var{filename} in the
 | 
						|
restricted environment's \module{__main__} module.
 | 
						|
\end{methoddesc}
 | 
						|
 | 
						|
Methods whose names begin with \samp{s_} are similar to the functions
 | 
						|
beginning with \samp{r_}, but the code will be granted access to
 | 
						|
restricted versions of the standard I/O streams \code{sys.stdin},
 | 
						|
\code{sys.stderr}, and \code{sys.stdout}.
 | 
						|
 | 
						|
\begin{methoddesc}{s_eval}{code}
 | 
						|
\var{code} must be a string containing a Python expression, which will
 | 
						|
be evaluated in the restricted environment.  
 | 
						|
\end{methoddesc}
 | 
						|
 | 
						|
\begin{methoddesc}{s_exec}{code}
 | 
						|
\var{code} must be a string containing one or more lines of Python code,
 | 
						|
which will be executed in the restricted environment.  
 | 
						|
\end{methoddesc}
 | 
						|
 | 
						|
\begin{methoddesc}{s_execfile}{code}
 | 
						|
Execute the Python code contained in the file \var{filename} in the
 | 
						|
restricted environment.
 | 
						|
\end{methoddesc}
 | 
						|
 | 
						|
\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.
 | 
						|
 | 
						|
\begin{methoddesc}{r_import}{modulename\optional{, globals\optional{,
 | 
						|
                             locals\optional{, fromlist}}}}
 | 
						|
Import the module \var{modulename}, raising an \exception{ImportError}
 | 
						|
exception if the module is considered unsafe.
 | 
						|
\end{methoddesc}
 | 
						|
 | 
						|
\begin{methoddesc}{r_open}{filename\optional{, mode\optional{, bufsize}}}
 | 
						|
Method called when \function{open()} is called in the restricted
 | 
						|
environment.  The arguments are identical to those of \function{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
 | 
						|
\method{r_open()}.
 | 
						|
\end{methoddesc}
 | 
						|
 | 
						|
\begin{methoddesc}{r_reload}{module}
 | 
						|
Reload the module object \var{module}, re-parsing and re-initializing it.  
 | 
						|
\end{methoddesc}
 | 
						|
 | 
						|
\begin{methoddesc}{r_unload}{module}
 | 
						|
Unload the module object \var{module} (remove it from the
 | 
						|
restricted environment's \code{sys.modules} dictionary).
 | 
						|
\end{methoddesc}
 | 
						|
 | 
						|
And their equivalents with access to restricted standard I/O streams:
 | 
						|
 | 
						|
\begin{methoddesc}{s_import}{modulename\optional{, globals\optional{,
 | 
						|
                             locals\optional{, fromlist}}}}
 | 
						|
Import the module \var{modulename}, raising an \exception{ImportError}
 | 
						|
exception if the module is considered unsafe.
 | 
						|
\end{methoddesc}
 | 
						|
 | 
						|
\begin{methoddesc}{s_reload}{module}
 | 
						|
Reload the module object \var{module}, re-parsing and re-initializing it.  
 | 
						|
\end{methoddesc}
 | 
						|
 | 
						|
\begin{methoddesc}{s_unload}{module}
 | 
						|
Unload the module object \var{module}.   
 | 
						|
% XXX what are the semantics of this?  
 | 
						|
\end{methoddesc}
 | 
						|
 | 
						|
 | 
						|
\subsection{Defining restricted environments \label{rexec-extension}}
 | 
						|
 | 
						|
The \class{RExec} class has the following class attributes, which are
 | 
						|
used by the \method{__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.
 | 
						|
 | 
						|
\begin{memberdesc}{nok_builtin_names}
 | 
						|
Contains the names of built-in functions which will \emph{not} be
 | 
						|
available to programs running in the restricted environment.  The
 | 
						|
value for \class{RExec} is \code{('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.)
 | 
						|
\end{memberdesc}
 | 
						|
 | 
						|
\begin{memberdesc}{ok_builtin_modules}
 | 
						|
Contains the names of built-in modules which can be safely imported.
 | 
						|
The value for \class{RExec} is \code{('audioop', 'array', 'binascii',
 | 
						|
'cmath', 'errno', 'imageop', 'marshal', 'math', 'md5', 'operator',
 | 
						|
'parser', 'regex', 'rotor', '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.
 | 
						|
\end{memberdesc}
 | 
						|
 | 
						|
\begin{memberdesc}{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 \code{sys.path} (at the time
 | 
						|
the module is loaded) for unrestricted code.
 | 
						|
\end{memberdesc}
 | 
						|
 | 
						|
\begin{memberdesc}{ok_posix_names}
 | 
						|
% Should this be called ok_os_names?
 | 
						|
Contains the names of the functions in the \refmodule{os} module which will be
 | 
						|
available to programs running in the restricted environment.  The
 | 
						|
value for \class{RExec} is \code{('error', 'fstat', 'listdir',
 | 
						|
'lstat', 'readlink', 'stat', 'times', 'uname', 'getpid', 'getppid',
 | 
						|
'getcwd', 'getuid', 'getgid', 'geteuid', 'getegid')}.
 | 
						|
\end{memberdesc}
 | 
						|
 | 
						|
\begin{memberdesc}{ok_sys_names}
 | 
						|
Contains the names of the functions and variables in the \refmodule{sys}
 | 
						|
module which will be available to programs running in the restricted
 | 
						|
environment.  The value for \class{RExec} is \code{('ps1', 'ps2',
 | 
						|
'copyright', 'version', 'platform', 'exit', 'maxint')}.
 | 
						|
\end{memberdesc}
 | 
						|
 | 
						|
 | 
						|
\subsection{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:
 | 
						|
 | 
						|
\begin{verbatim}
 | 
						|
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)
 | 
						|
\end{verbatim}
 | 
						|
%
 | 
						|
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
 | 
						|
\method{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.
 |