blob: 3f04391cd723887bff2c1dd2fd4a24aa0e08b137 [file] [log] [blame]
Guido van Rossum40d1ea31995-08-04 03:59:03 +00001"""Restricted execution facilities.
Guido van Rossum9a22de11995-01-12 12:29:47 +00002
Guido van Rossum40d1ea31995-08-04 03:59:03 +00003The class RExec exports methods rexec(), reval(), rexecfile(), and
4import_module(), which correspond roughly to the built-in operations
5exec, eval(), execfile() and import, but executing the code in an
6environment that only exposes those built-in operations that are
7deemed safe. To this end, a modest collection of 'fake' modules is
8created which mimics the standard modules by the same names. It is a
9policy decision which built-in modules and operations are made
10available; this module provides a reasonable default, but derived
11classes can change the policies e.g. by overriding or extending class
12variables like ok_builtin_modules or methods like make_sys().
13
14"""
15
16
Guido van Rossum9a22de11995-01-12 12:29:47 +000017import sys
Guido van Rossum40d1ea31995-08-04 03:59:03 +000018import __builtin__
19import os
20import marshal
21import ihooks
Guido van Rossum9a22de11995-01-12 12:29:47 +000022
Guido van Rossum9a22de11995-01-12 12:29:47 +000023
Guido van Rossum40d1ea31995-08-04 03:59:03 +000024class RHooks(ihooks.Hooks):
Guido van Rossum9a22de11995-01-12 12:29:47 +000025
Guido van Rossum40d1ea31995-08-04 03:59:03 +000026 def __init__(self, rexec, verbose=0):
27 ihooks.Hooks.__init__(self, verbose)
28 self.rexec = rexec
Guido van Rossum9a22de11995-01-12 12:29:47 +000029
Guido van Rossum40d1ea31995-08-04 03:59:03 +000030 def is_builtin(self, name):
31 return self.rexec.is_builtin(name)
Guido van Rossum9a22de11995-01-12 12:29:47 +000032
Guido van Rossum40d1ea31995-08-04 03:59:03 +000033 def init_builtin(self, name):
34 m = __import__(name)
35 return self.rexec.copy_except(m, ())
Guido van Rossum9a22de11995-01-12 12:29:47 +000036
Guido van Rossum40d1ea31995-08-04 03:59:03 +000037 def init_frozen(self, name): raise SystemError, "don't use this"
38 def load_source(self, *args): raise SystemError, "don't use this"
39 def load_compiled(self, *args): raise SystemError, "don't use this"
40
41 def load_dynamic(self, *args):
42 raise ImportError, "import of dynamically loaded modules not allowed"
43
44 def add_module(self, name):
45 return self.rexec.add_module(name)
46
47 def modules_dict(self):
48 return self.rexec.modules
49
50 def default_path(self):
51 return self.rexec.modules['sys'].path
52
53
54class RModuleLoader(ihooks.FancyModuleLoader):
55
56 pass
57
58
59class RModuleImporter(ihooks.ModuleImporter):
60
61 pass
62
63
64class RExec(ihooks._Verbose):
65
66 """Restricted Execution environment."""
67
68 ok_path = tuple(sys.path) # That's a policy decision
69
70 ok_builtin_modules = ('array', 'audioop', 'imageop', 'marshal', 'math',
71 'md5', 'parser', 'regex', 'rotor', 'select',
72 'strop', 'struct', 'time')
73
74 ok_posix_names = ('error', 'fstat', 'listdir', 'lstat', 'readlink',
75 'stat', 'times', 'uname', 'getpid', 'getppid',
76 'getcwd', 'getuid', 'getgid', 'geteuid', 'getegid')
77
78 ok_sys_names = ('ps1', 'ps2', 'copyright', 'version',
79 'platform', 'exit', 'maxint')
80
81 nok_builtin_names = ('open', 'reload', '__import__', 'raw_input', 'input')
82
83 def __init__(self, hooks = None, verbose = 0):
84 ihooks._Verbose.__init__(self, verbose)
85 # XXX There's a circular reference here:
86 self.hooks = hooks or RHooks(self, verbose)
87 self.modules = {}
88 self.ok_builtin_modules = map(None, filter(
89 lambda mname: mname in sys.builtin_module_names,
90 self.ok_builtin_modules))
91 self.make_builtin()
92 self.make_initial_modules()
93 # make_sys must be last because it adds the already created
94 # modules to its builtin_module_names
95 self.make_sys()
96 self.loader = RModuleLoader(self.hooks, verbose)
97 self.importer = RModuleImporter(self.loader, verbose)
98
99 def make_initial_modules(self):
100 self.make_main()
101 self.make_osname()
102
103 # Helpers for RHooks
104
105 def is_builtin(self, mname):
106 return mname in self.ok_builtin_modules
107
108 # The make_* methods create specific built-in modules
109
110 def make_builtin(self):
111 m = self.copy_except(__builtin__, self.nok_builtin_names)
112 m.__import__ = self.r_import
113
114 def make_main(self):
115 m = self.add_module('__main__')
116
117 def make_osname(self):
118 osname = os.name
119 src = __import__(osname)
120 dst = self.copy_only(src, self.ok_posix_names)
121 dst.environ = e = {}
122 for key, value in os.environ.items():
123 e[key] = value
124
125 def make_sys(self):
126 m = self.copy_only(sys, self.ok_sys_names)
127 m.modules = self.modules
128 m.argv = ['RESTRICTED']
129 m.path = map(None, self.ok_path)
130 m = self.modules['sys']
131 m.builtin_module_names = \
132 self.modules.keys() + self.ok_builtin_modules
133 m.builtin_module_names.sort()
134
135 # The copy_* methods copy existing modules with some changes
136
137 def copy_except(self, src, exceptions):
138 dst = self.copy_none(src)
139 for name in dir(src):
140 if name not in exceptions:
141 setattr(dst, name, getattr(src, name))
142 return dst
143
144 def copy_only(self, src, names):
145 dst = self.copy_none(src)
146 for name in names:
147 try:
148 value = getattr(src, name)
149 except AttributeError:
150 continue
151 setattr(dst, name, value)
152 return dst
153
154 def copy_none(self, src):
155 return self.add_module(src.__name__)
156
157 # Add a module -- return an existing module or create one
158
159 def add_module(self, mname):
160 if self.modules.has_key(mname):
161 return self.modules[mname]
162 self.modules[mname] = m = self.hooks.new_module(mname)
163 m.__builtins__ = self.modules['__builtin__']
Guido van Rossum9a22de11995-01-12 12:29:47 +0000164 return m
165
Guido van Rossum40d1ea31995-08-04 03:59:03 +0000166 # The r* methods are public interfaces
Guido van Rossum9a22de11995-01-12 12:29:47 +0000167
Guido van Rossum40d1ea31995-08-04 03:59:03 +0000168 def r_exec(self, code):
169 m = self.add_module('__main__')
170 exec code in m.__dict__
Guido van Rossum9a22de11995-01-12 12:29:47 +0000171
Guido van Rossum40d1ea31995-08-04 03:59:03 +0000172 def r_eval(self, code):
173 m = self.add_module('__main__')
174 return eval(code, m.__dict__)
Guido van Rossum9a22de11995-01-12 12:29:47 +0000175
Guido van Rossum40d1ea31995-08-04 03:59:03 +0000176 def r_execfile(self, file):
177 m = self.add_module('__main__')
178 return execfile(file, m.__dict__)
Guido van Rossum9a22de11995-01-12 12:29:47 +0000179
Guido van Rossum40d1ea31995-08-04 03:59:03 +0000180 def r_import(self, mname, globals={}, locals={}, fromlist=[]):
181 return self.importer.import_module(mname, globals, locals, fromlist)
Guido van Rossum9a22de11995-01-12 12:29:47 +0000182
183
184def test():
Guido van Rossum40d1ea31995-08-04 03:59:03 +0000185 import traceback
186 r = RExec(None, '-v' in sys.argv[1:])
187 print "*** RESTRICTED *** Python", sys.version
188 print sys.copyright
189 while 1:
190 try:
191 try:
192 s = raw_input('>>> ')
193 except EOFError:
194 print
195 break
196 if s and s[0] != '#':
197 s = s + '\n'
198 c = compile(s, '<stdin>', 'single')
199 r.r_exec(c)
200 except SystemExit, n:
201 sys.exit(n)
202 except:
203 traceback.print_exc()
204
Guido van Rossum9a22de11995-01-12 12:29:47 +0000205
206if __name__ == '__main__':
Guido van Rossum40d1ea31995-08-04 03:59:03 +0000207 test()