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