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