symbian-qemu-0.9.1-12/python-2.6.1/Lib/rexec.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 """Restricted execution facilities.
       
     2 
       
     3 The class RExec exports methods r_exec(), r_eval(), r_execfile(), and
       
     4 r_import(), which correspond roughly to the built-in operations
       
     5 exec, eval(), execfile() and import, but executing the code in an
       
     6 environment that only exposes those built-in operations that are
       
     7 deemed safe.  To this end, a modest collection of 'fake' modules is
       
     8 created which mimics the standard modules by the same names.  It is a
       
     9 policy decision which built-in modules and operations are made
       
    10 available; this module provides a reasonable default, but derived
       
    11 classes can change the policies e.g. by overriding or extending class
       
    12 variables like ok_builtin_modules or methods like make_sys().
       
    13 
       
    14 XXX To do:
       
    15 - r_open should allow writing tmp dir
       
    16 - r_exec etc. with explicit globals/locals? (Use rexec("exec ... in ...")?)
       
    17 
       
    18 """
       
    19 from warnings import warnpy3k
       
    20 warnpy3k("the rexec module has been removed in Python 3.0", stacklevel=2)
       
    21 del warnpy3k
       
    22 
       
    23 
       
    24 import sys
       
    25 import __builtin__
       
    26 import os
       
    27 import ihooks
       
    28 import imp
       
    29 
       
    30 __all__ = ["RExec"]
       
    31 
       
    32 class FileBase:
       
    33 
       
    34     ok_file_methods = ('fileno', 'flush', 'isatty', 'read', 'readline',
       
    35             'readlines', 'seek', 'tell', 'write', 'writelines', 'xreadlines',
       
    36             '__iter__')
       
    37 
       
    38 
       
    39 class FileWrapper(FileBase):
       
    40 
       
    41     # XXX This is just like a Bastion -- should use that!
       
    42 
       
    43     def __init__(self, f):
       
    44         for m in self.ok_file_methods:
       
    45             if not hasattr(self, m) and hasattr(f, m):
       
    46                 setattr(self, m, getattr(f, m))
       
    47 
       
    48     def close(self):
       
    49         self.flush()
       
    50 
       
    51 
       
    52 TEMPLATE = """
       
    53 def %s(self, *args):
       
    54         return getattr(self.mod, self.name).%s(*args)
       
    55 """
       
    56 
       
    57 class FileDelegate(FileBase):
       
    58 
       
    59     def __init__(self, mod, name):
       
    60         self.mod = mod
       
    61         self.name = name
       
    62 
       
    63     for m in FileBase.ok_file_methods + ('close',):
       
    64         exec TEMPLATE % (m, m)
       
    65 
       
    66 
       
    67 class RHooks(ihooks.Hooks):
       
    68 
       
    69     def __init__(self, *args):
       
    70         # Hacks to support both old and new interfaces:
       
    71         # old interface was RHooks(rexec[, verbose])
       
    72         # new interface is RHooks([verbose])
       
    73         verbose = 0
       
    74         rexec = None
       
    75         if args and type(args[-1]) == type(0):
       
    76             verbose = args[-1]
       
    77             args = args[:-1]
       
    78         if args and hasattr(args[0], '__class__'):
       
    79             rexec = args[0]
       
    80             args = args[1:]
       
    81         if args:
       
    82             raise TypeError, "too many arguments"
       
    83         ihooks.Hooks.__init__(self, verbose)
       
    84         self.rexec = rexec
       
    85 
       
    86     def set_rexec(self, rexec):
       
    87         # Called by RExec instance to complete initialization
       
    88         self.rexec = rexec
       
    89 
       
    90     def get_suffixes(self):
       
    91         return self.rexec.get_suffixes()
       
    92 
       
    93     def is_builtin(self, name):
       
    94         return self.rexec.is_builtin(name)
       
    95 
       
    96     def init_builtin(self, name):
       
    97         m = __import__(name)
       
    98         return self.rexec.copy_except(m, ())
       
    99 
       
   100     def init_frozen(self, name): raise SystemError, "don't use this"
       
   101     def load_source(self, *args): raise SystemError, "don't use this"
       
   102     def load_compiled(self, *args): raise SystemError, "don't use this"
       
   103     def load_package(self, *args): raise SystemError, "don't use this"
       
   104 
       
   105     def load_dynamic(self, name, filename, file):
       
   106         return self.rexec.load_dynamic(name, filename, file)
       
   107 
       
   108     def add_module(self, name):
       
   109         return self.rexec.add_module(name)
       
   110 
       
   111     def modules_dict(self):
       
   112         return self.rexec.modules
       
   113 
       
   114     def default_path(self):
       
   115         return self.rexec.modules['sys'].path
       
   116 
       
   117 
       
   118 # XXX Backwards compatibility
       
   119 RModuleLoader = ihooks.FancyModuleLoader
       
   120 RModuleImporter = ihooks.ModuleImporter
       
   121 
       
   122 
       
   123 class RExec(ihooks._Verbose):
       
   124     """Basic restricted execution framework.
       
   125 
       
   126     Code executed in this restricted environment will only have access to
       
   127     modules and functions that are deemed safe; you can subclass RExec to
       
   128     add or remove capabilities as desired.
       
   129 
       
   130     The RExec class can prevent code from performing unsafe operations like
       
   131     reading or writing disk files, or using TCP/IP sockets.  However, it does
       
   132     not protect against code using extremely large amounts of memory or
       
   133     processor time.
       
   134 
       
   135     """
       
   136 
       
   137     ok_path = tuple(sys.path)           # That's a policy decision
       
   138 
       
   139     ok_builtin_modules = ('audioop', 'array', 'binascii',
       
   140                           'cmath', 'errno', 'imageop',
       
   141                           'marshal', 'math', 'md5', 'operator',
       
   142                           'parser', 'select',
       
   143                           'sha', '_sre', 'strop', 'struct', 'time',
       
   144                           '_weakref')
       
   145 
       
   146     ok_posix_names = ('error', 'fstat', 'listdir', 'lstat', 'readlink',
       
   147                       'stat', 'times', 'uname', 'getpid', 'getppid',
       
   148                       'getcwd', 'getuid', 'getgid', 'geteuid', 'getegid')
       
   149 
       
   150     ok_sys_names = ('byteorder', 'copyright', 'exit', 'getdefaultencoding',
       
   151                     'getrefcount', 'hexversion', 'maxint', 'maxunicode',
       
   152                     'platform', 'ps1', 'ps2', 'version', 'version_info')
       
   153 
       
   154     nok_builtin_names = ('open', 'file', 'reload', '__import__')
       
   155 
       
   156     ok_file_types = (imp.C_EXTENSION, imp.PY_SOURCE)
       
   157 
       
   158     def __init__(self, hooks = None, verbose = 0):
       
   159         """Returns an instance of the RExec class.
       
   160 
       
   161         The hooks parameter is an instance of the RHooks class or a subclass
       
   162         of it.  If it is omitted or None, the default RHooks class is
       
   163         instantiated.
       
   164 
       
   165         Whenever the RExec module searches for a module (even a built-in one)
       
   166         or reads a module's code, it doesn't actually go out to the file
       
   167         system itself.  Rather, it calls methods of an RHooks instance that
       
   168         was passed to or created by its constructor.  (Actually, the RExec
       
   169         object doesn't make these calls --- they are made by a module loader
       
   170         object that's part of the RExec object.  This allows another level of
       
   171         flexibility, which can be useful when changing the mechanics of
       
   172         import within the restricted environment.)
       
   173 
       
   174         By providing an alternate RHooks object, we can control the file
       
   175         system accesses made to import a module, without changing the
       
   176         actual algorithm that controls the order in which those accesses are
       
   177         made.  For instance, we could substitute an RHooks object that
       
   178         passes all filesystem requests to a file server elsewhere, via some
       
   179         RPC mechanism such as ILU.  Grail's applet loader uses this to support
       
   180         importing applets from a URL for a directory.
       
   181 
       
   182         If the verbose parameter is true, additional debugging output may be
       
   183         sent to standard output.
       
   184 
       
   185         """
       
   186 
       
   187         raise RuntimeError, "This code is not secure in Python 2.2 and later"
       
   188 
       
   189         ihooks._Verbose.__init__(self, verbose)
       
   190         # XXX There's a circular reference here:
       
   191         self.hooks = hooks or RHooks(verbose)
       
   192         self.hooks.set_rexec(self)
       
   193         self.modules = {}
       
   194         self.ok_dynamic_modules = self.ok_builtin_modules
       
   195         list = []
       
   196         for mname in self.ok_builtin_modules:
       
   197             if mname in sys.builtin_module_names:
       
   198                 list.append(mname)
       
   199         self.ok_builtin_modules = tuple(list)
       
   200         self.set_trusted_path()
       
   201         self.make_builtin()
       
   202         self.make_initial_modules()
       
   203         # make_sys must be last because it adds the already created
       
   204         # modules to its builtin_module_names
       
   205         self.make_sys()
       
   206         self.loader = RModuleLoader(self.hooks, verbose)
       
   207         self.importer = RModuleImporter(self.loader, verbose)
       
   208 
       
   209     def set_trusted_path(self):
       
   210         # Set the path from which dynamic modules may be loaded.
       
   211         # Those dynamic modules must also occur in ok_builtin_modules
       
   212         self.trusted_path = filter(os.path.isabs, sys.path)
       
   213 
       
   214     def load_dynamic(self, name, filename, file):
       
   215         if name not in self.ok_dynamic_modules:
       
   216             raise ImportError, "untrusted dynamic module: %s" % name
       
   217         if name in sys.modules:
       
   218             src = sys.modules[name]
       
   219         else:
       
   220             src = imp.load_dynamic(name, filename, file)
       
   221         dst = self.copy_except(src, [])
       
   222         return dst
       
   223 
       
   224     def make_initial_modules(self):
       
   225         self.make_main()
       
   226         self.make_osname()
       
   227 
       
   228     # Helpers for RHooks
       
   229 
       
   230     def get_suffixes(self):
       
   231         return [item   # (suff, mode, type)
       
   232                 for item in imp.get_suffixes()
       
   233                 if item[2] in self.ok_file_types]
       
   234 
       
   235     def is_builtin(self, mname):
       
   236         return mname in self.ok_builtin_modules
       
   237 
       
   238     # The make_* methods create specific built-in modules
       
   239 
       
   240     def make_builtin(self):
       
   241         m = self.copy_except(__builtin__, self.nok_builtin_names)
       
   242         m.__import__ = self.r_import
       
   243         m.reload = self.r_reload
       
   244         m.open = m.file = self.r_open
       
   245 
       
   246     def make_main(self):
       
   247         m = self.add_module('__main__')
       
   248 
       
   249     def make_osname(self):
       
   250         osname = os.name
       
   251         src = __import__(osname)
       
   252         dst = self.copy_only(src, self.ok_posix_names)
       
   253         dst.environ = e = {}
       
   254         for key, value in os.environ.items():
       
   255             e[key] = value
       
   256 
       
   257     def make_sys(self):
       
   258         m = self.copy_only(sys, self.ok_sys_names)
       
   259         m.modules = self.modules
       
   260         m.argv = ['RESTRICTED']
       
   261         m.path = map(None, self.ok_path)
       
   262         m.exc_info = self.r_exc_info
       
   263         m = self.modules['sys']
       
   264         l = self.modules.keys() + list(self.ok_builtin_modules)
       
   265         l.sort()
       
   266         m.builtin_module_names = tuple(l)
       
   267 
       
   268     # The copy_* methods copy existing modules with some changes
       
   269 
       
   270     def copy_except(self, src, exceptions):
       
   271         dst = self.copy_none(src)
       
   272         for name in dir(src):
       
   273             setattr(dst, name, getattr(src, name))
       
   274         for name in exceptions:
       
   275             try:
       
   276                 delattr(dst, name)
       
   277             except AttributeError:
       
   278                 pass
       
   279         return dst
       
   280 
       
   281     def copy_only(self, src, names):
       
   282         dst = self.copy_none(src)
       
   283         for name in names:
       
   284             try:
       
   285                 value = getattr(src, name)
       
   286             except AttributeError:
       
   287                 continue
       
   288             setattr(dst, name, value)
       
   289         return dst
       
   290 
       
   291     def copy_none(self, src):
       
   292         m = self.add_module(src.__name__)
       
   293         m.__doc__ = src.__doc__
       
   294         return m
       
   295 
       
   296     # Add a module -- return an existing module or create one
       
   297 
       
   298     def add_module(self, mname):
       
   299         m = self.modules.get(mname)
       
   300         if m is None:
       
   301             self.modules[mname] = m = self.hooks.new_module(mname)
       
   302         m.__builtins__ = self.modules['__builtin__']
       
   303         return m
       
   304 
       
   305     # The r* methods are public interfaces
       
   306 
       
   307     def r_exec(self, code):
       
   308         """Execute code within a restricted environment.
       
   309 
       
   310         The code parameter must either be a string containing one or more
       
   311         lines of Python code, or a compiled code object, which will be
       
   312         executed in the restricted environment's __main__ module.
       
   313 
       
   314         """
       
   315         m = self.add_module('__main__')
       
   316         exec code in m.__dict__
       
   317 
       
   318     def r_eval(self, code):
       
   319         """Evaluate code within a restricted environment.
       
   320 
       
   321         The code parameter must either be a string containing a Python
       
   322         expression, or a compiled code object, which will be evaluated in
       
   323         the restricted environment's __main__ module.  The value of the
       
   324         expression or code object will be returned.
       
   325 
       
   326         """
       
   327         m = self.add_module('__main__')
       
   328         return eval(code, m.__dict__)
       
   329 
       
   330     def r_execfile(self, file):
       
   331         """Execute the Python code in the file in the restricted
       
   332         environment's __main__ module.
       
   333 
       
   334         """
       
   335         m = self.add_module('__main__')
       
   336         execfile(file, m.__dict__)
       
   337 
       
   338     def r_import(self, mname, globals={}, locals={}, fromlist=[]):
       
   339         """Import a module, raising an ImportError exception if the module
       
   340         is considered unsafe.
       
   341 
       
   342         This method is implicitly called by code executing in the
       
   343         restricted environment.  Overriding this method in a subclass is
       
   344         used to change the policies enforced by a restricted environment.
       
   345 
       
   346         """
       
   347         return self.importer.import_module(mname, globals, locals, fromlist)
       
   348 
       
   349     def r_reload(self, m):
       
   350         """Reload the module object, re-parsing and re-initializing it.
       
   351 
       
   352         This method is implicitly called by code executing in the
       
   353         restricted environment.  Overriding this method in a subclass is
       
   354         used to change the policies enforced by a restricted environment.
       
   355 
       
   356         """
       
   357         return self.importer.reload(m)
       
   358 
       
   359     def r_unload(self, m):
       
   360         """Unload the module.
       
   361 
       
   362         Removes it from the restricted environment's sys.modules dictionary.
       
   363 
       
   364         This method is implicitly called by code executing in the
       
   365         restricted environment.  Overriding this method in a subclass is
       
   366         used to change the policies enforced by a restricted environment.
       
   367 
       
   368         """
       
   369         return self.importer.unload(m)
       
   370 
       
   371     # The s_* methods are similar but also swap std{in,out,err}
       
   372 
       
   373     def make_delegate_files(self):
       
   374         s = self.modules['sys']
       
   375         self.delegate_stdin = FileDelegate(s, 'stdin')
       
   376         self.delegate_stdout = FileDelegate(s, 'stdout')
       
   377         self.delegate_stderr = FileDelegate(s, 'stderr')
       
   378         self.restricted_stdin = FileWrapper(sys.stdin)
       
   379         self.restricted_stdout = FileWrapper(sys.stdout)
       
   380         self.restricted_stderr = FileWrapper(sys.stderr)
       
   381 
       
   382     def set_files(self):
       
   383         if not hasattr(self, 'save_stdin'):
       
   384             self.save_files()
       
   385         if not hasattr(self, 'delegate_stdin'):
       
   386             self.make_delegate_files()
       
   387         s = self.modules['sys']
       
   388         s.stdin = self.restricted_stdin
       
   389         s.stdout = self.restricted_stdout
       
   390         s.stderr = self.restricted_stderr
       
   391         sys.stdin = self.delegate_stdin
       
   392         sys.stdout = self.delegate_stdout
       
   393         sys.stderr = self.delegate_stderr
       
   394 
       
   395     def reset_files(self):
       
   396         self.restore_files()
       
   397         s = self.modules['sys']
       
   398         self.restricted_stdin = s.stdin
       
   399         self.restricted_stdout = s.stdout
       
   400         self.restricted_stderr = s.stderr
       
   401 
       
   402 
       
   403     def save_files(self):
       
   404         self.save_stdin = sys.stdin
       
   405         self.save_stdout = sys.stdout
       
   406         self.save_stderr = sys.stderr
       
   407 
       
   408     def restore_files(self):
       
   409         sys.stdin = self.save_stdin
       
   410         sys.stdout = self.save_stdout
       
   411         sys.stderr = self.save_stderr
       
   412 
       
   413     def s_apply(self, func, args=(), kw={}):
       
   414         self.save_files()
       
   415         try:
       
   416             self.set_files()
       
   417             r = func(*args, **kw)
       
   418         finally:
       
   419             self.restore_files()
       
   420         return r
       
   421 
       
   422     def s_exec(self, *args):
       
   423         """Execute code within a restricted environment.
       
   424 
       
   425         Similar to the r_exec() method, but the code will be granted access
       
   426         to restricted versions of the standard I/O streams sys.stdin,
       
   427         sys.stderr, and sys.stdout.
       
   428 
       
   429         The code parameter must either be a string containing one or more
       
   430         lines of Python code, or a compiled code object, which will be
       
   431         executed in the restricted environment's __main__ module.
       
   432 
       
   433         """
       
   434         return self.s_apply(self.r_exec, args)
       
   435 
       
   436     def s_eval(self, *args):
       
   437         """Evaluate code within a restricted environment.
       
   438 
       
   439         Similar to the r_eval() method, but the code will be granted access
       
   440         to restricted versions of the standard I/O streams sys.stdin,
       
   441         sys.stderr, and sys.stdout.
       
   442 
       
   443         The code parameter must either be a string containing a Python
       
   444         expression, or a compiled code object, which will be evaluated in
       
   445         the restricted environment's __main__ module.  The value of the
       
   446         expression or code object will be returned.
       
   447 
       
   448         """
       
   449         return self.s_apply(self.r_eval, args)
       
   450 
       
   451     def s_execfile(self, *args):
       
   452         """Execute the Python code in the file in the restricted
       
   453         environment's __main__ module.
       
   454 
       
   455         Similar to the r_execfile() method, but the code will be granted
       
   456         access to restricted versions of the standard I/O streams sys.stdin,
       
   457         sys.stderr, and sys.stdout.
       
   458 
       
   459         """
       
   460         return self.s_apply(self.r_execfile, args)
       
   461 
       
   462     def s_import(self, *args):
       
   463         """Import a module, raising an ImportError exception if the module
       
   464         is considered unsafe.
       
   465 
       
   466         This method is implicitly called by code executing in the
       
   467         restricted environment.  Overriding this method in a subclass is
       
   468         used to change the policies enforced by a restricted environment.
       
   469 
       
   470         Similar to the r_import() method, but has access to restricted
       
   471         versions of the standard I/O streams sys.stdin, sys.stderr, and
       
   472         sys.stdout.
       
   473 
       
   474         """
       
   475         return self.s_apply(self.r_import, args)
       
   476 
       
   477     def s_reload(self, *args):
       
   478         """Reload the module object, re-parsing and re-initializing it.
       
   479 
       
   480         This method is implicitly called by code executing in the
       
   481         restricted environment.  Overriding this method in a subclass is
       
   482         used to change the policies enforced by a restricted environment.
       
   483 
       
   484         Similar to the r_reload() method, but has access to restricted
       
   485         versions of the standard I/O streams sys.stdin, sys.stderr, and
       
   486         sys.stdout.
       
   487 
       
   488         """
       
   489         return self.s_apply(self.r_reload, args)
       
   490 
       
   491     def s_unload(self, *args):
       
   492         """Unload the module.
       
   493 
       
   494         Removes it from the restricted environment's sys.modules dictionary.
       
   495 
       
   496         This method is implicitly called by code executing in the
       
   497         restricted environment.  Overriding this method in a subclass is
       
   498         used to change the policies enforced by a restricted environment.
       
   499 
       
   500         Similar to the r_unload() method, but has access to restricted
       
   501         versions of the standard I/O streams sys.stdin, sys.stderr, and
       
   502         sys.stdout.
       
   503 
       
   504         """
       
   505         return self.s_apply(self.r_unload, args)
       
   506 
       
   507     # Restricted open(...)
       
   508 
       
   509     def r_open(self, file, mode='r', buf=-1):
       
   510         """Method called when open() is called in the restricted environment.
       
   511 
       
   512         The arguments are identical to those of the open() function, and a
       
   513         file object (or a class instance compatible with file objects)
       
   514         should be returned.  RExec's default behaviour is allow opening
       
   515         any file for reading, but forbidding any attempt to write a file.
       
   516 
       
   517         This method is implicitly called by code executing in the
       
   518         restricted environment.  Overriding this method in a subclass is
       
   519         used to change the policies enforced by a restricted environment.
       
   520 
       
   521         """
       
   522         mode = str(mode)
       
   523         if mode not in ('r', 'rb'):
       
   524             raise IOError, "can't open files for writing in restricted mode"
       
   525         return open(file, mode, buf)
       
   526 
       
   527     # Restricted version of sys.exc_info()
       
   528 
       
   529     def r_exc_info(self):
       
   530         ty, va, tr = sys.exc_info()
       
   531         tr = None
       
   532         return ty, va, tr
       
   533 
       
   534 
       
   535 def test():
       
   536     import getopt, traceback
       
   537     opts, args = getopt.getopt(sys.argv[1:], 'vt:')
       
   538     verbose = 0
       
   539     trusted = []
       
   540     for o, a in opts:
       
   541         if o == '-v':
       
   542             verbose = verbose+1
       
   543         if o == '-t':
       
   544             trusted.append(a)
       
   545     r = RExec(verbose=verbose)
       
   546     if trusted:
       
   547         r.ok_builtin_modules = r.ok_builtin_modules + tuple(trusted)
       
   548     if args:
       
   549         r.modules['sys'].argv = args
       
   550         r.modules['sys'].path.insert(0, os.path.dirname(args[0]))
       
   551     else:
       
   552         r.modules['sys'].path.insert(0, "")
       
   553     fp = sys.stdin
       
   554     if args and args[0] != '-':
       
   555         try:
       
   556             fp = open(args[0])
       
   557         except IOError, msg:
       
   558             print "%s: can't open file %r" % (sys.argv[0], args[0])
       
   559             return 1
       
   560     if fp.isatty():
       
   561         try:
       
   562             import readline
       
   563         except ImportError:
       
   564             pass
       
   565         import code
       
   566         class RestrictedConsole(code.InteractiveConsole):
       
   567             def runcode(self, co):
       
   568                 self.locals['__builtins__'] = r.modules['__builtin__']
       
   569                 r.s_apply(code.InteractiveConsole.runcode, (self, co))
       
   570         try:
       
   571             RestrictedConsole(r.modules['__main__'].__dict__).interact()
       
   572         except SystemExit, n:
       
   573             return n
       
   574     else:
       
   575         text = fp.read()
       
   576         fp.close()
       
   577         c = compile(text, fp.name, 'exec')
       
   578         try:
       
   579             r.s_exec(c)
       
   580         except SystemExit, n:
       
   581             return n
       
   582         except:
       
   583             traceback.print_exc()
       
   584             return 1
       
   585 
       
   586 
       
   587 if __name__ == '__main__':
       
   588     sys.exit(test())