From 8fa45677c1833cc0d4ddaa57417c01ee8297eba8 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Thu, 13 Sep 2001 21:01:29 +0000 Subject: Now that file objects are subclassable, you can get at the file constructor just by doing type(f) where f is any file object. This left a hole in restricted execution mode that rexec.py can't plug by itself (although it can plug part of it; the rest is plugged in fileobject.c now). --- Lib/rexec.py | 4 ++-- Lib/test/test_descr.py | 42 ++++++++++++++++++++++++++++++++++++++++++ Objects/fileobject.c | 8 ++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/Lib/rexec.py b/Lib/rexec.py index d1dd4eb..cece544 100644 --- a/Lib/rexec.py +++ b/Lib/rexec.py @@ -132,7 +132,7 @@ class RExec(ihooks._Verbose): ok_sys_names = ('ps1', 'ps2', 'copyright', 'version', 'platform', 'exit', 'maxint') - nok_builtin_names = ('open', 'reload', '__import__') + nok_builtin_names = ('open', 'file', 'reload', '__import__') def __init__(self, hooks = None, verbose = 0): ihooks._Verbose.__init__(self, verbose) @@ -186,7 +186,7 @@ class RExec(ihooks._Verbose): m = self.copy_except(__builtin__, self.nok_builtin_names) m.__import__ = self.r_import m.reload = self.r_reload - m.open = self.r_open + m.open = m.file = self.r_open def make_main(self): m = self.add_module('__main__') diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 02ef0ef..f1af5b9 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -1717,6 +1717,47 @@ def keywords(): raise TestFailed("expected TypeError from bogus keyword " "argument to %r" % constructor) +def restricted(): + import rexec + if verbose: + print "Testing interaction with restricted execution ..." + + sandbox = rexec.RExec() + + code1 = """f = open(%r, 'w')""" % TESTFN + code2 = """f = file(%r, 'w')""" % TESTFN + code3 = """\ +f = open(%r) +t = type(f) # a sneaky way to get the file() constructor +f.close() +f = t(%r, 'w') # rexec can't catch this by itself +""" % (TESTFN, TESTFN) + + f = open(TESTFN, 'w') # Create the file so code3 can find it. + f.close() + + try: + for code in code1, code2, code3: + try: + sandbox.r_exec(code) + except IOError, msg: + if str(msg).find("restricted") >= 0: + outcome = "OK" + else: + outcome = "got an exception, but not an expected one" + else: + outcome = "expected a restricted-execution exception" + + if outcome != "OK": + raise TestFailed("%s, in %r" % (outcome, code)) + + finally: + try: + import os + os.unlink(TESTFN) + except: + pass + def all(): lists() dicts() @@ -1752,6 +1793,7 @@ def all(): supers() inherits() keywords() + restricted() all() diff --git a/Objects/fileobject.c b/Objects/fileobject.c index 3cadff5..b373024 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -92,6 +92,14 @@ open_the_file(PyFileObject *f, char *name, char *mode) assert(name != NULL); assert(mode != NULL); + /* rexec.py can't stop a user from getting the file() constructor -- + all they have to do is get *any* file object f, and then do + type(f). Here we prevent them from doing damage with it. */ + if (PyEval_GetRestricted()) { + PyErr_SetString(PyExc_IOError, + "file() constructor not accessible in restricted mode"); + return NULL; + } #ifdef HAVE_FOPENRF if (*mode == '*') { FILE *fopenRF(); -- cgit v0.12