[Zope-Checkins] SVN: Zope/trunk/ - Forward-port fix for
vulnerability addressed in Hotfix_20050405 from 2.7 branch.
Tres Seaver
tseaver at zope.com
Tue Apr 5 16:58:31 EDT 2005
Log message for revision 29883:
- Forward-port fix for vulnerability addressed in Hotfix_20050405 from 2.7 branch.
Changed:
U Zope/trunk/doc/CHANGES.txt
U Zope/trunk/lib/python/AccessControl/ZopeGuards.py
U Zope/trunk/lib/python/AccessControl/tests/testZopeGuards.py
U Zope/trunk/lib/python/RestrictedPython/RestrictionMutator.py
-=-
Modified: Zope/trunk/doc/CHANGES.txt
===================================================================
--- Zope/trunk/doc/CHANGES.txt 2005-04-05 20:40:42 UTC (rev 29882)
+++ Zope/trunk/doc/CHANGES.txt 2005-04-05 20:58:31 UTC (rev 29883)
@@ -30,6 +30,9 @@
Features added
Bugs fixed
+
+ - Hotfix_20050405: classes defined in untrusted code could shadow
+ the roles of methods defined as protected by their bases.
- Collector #1656: Fixed enumeration within untrusted code
(forward-port from 2.7 branch).
Modified: Zope/trunk/lib/python/AccessControl/ZopeGuards.py
===================================================================
--- Zope/trunk/lib/python/AccessControl/ZopeGuards.py 2005-04-05 20:40:42 UTC (rev 29882)
+++ Zope/trunk/lib/python/AccessControl/ZopeGuards.py 2005-04-05 20:58:31 UTC (rev 29883)
@@ -367,6 +367,9 @@
# This metaclass supplies the security declarations that allow all
# attributes of a class and its instances to be read and written.
def _metaclass(name, bases, dict):
+ for k, v in dict.items():
+ if k.endswith('__roles__') and k[:len('__roles__')] not in dict:
+ raise Unauthorized, "Can't override security: %s" % k
ob = type(name, bases, dict)
ob.__allow_access_to_unprotected_subobjects__ = 1
ob._guarded_writes = 1
Modified: Zope/trunk/lib/python/AccessControl/tests/testZopeGuards.py
===================================================================
--- Zope/trunk/lib/python/AccessControl/tests/testZopeGuards.py 2005-04-05 20:40:42 UTC (rev 29882)
+++ Zope/trunk/lib/python/AccessControl/tests/testZopeGuards.py 2005-04-05 20:58:31 UTC (rev 29883)
@@ -58,7 +58,6 @@
self.calls.append(('checkPermission', args))
return not self.reject
-
class GuardTestCase(unittest.TestCase):
def setSecurityManager(self, manager):
@@ -378,7 +377,82 @@
# the next one could be called an integration test. But we're simply
# trying to run restricted Python with the *intended* implementations of
# the special wrappers here, so no apologies.
+_ProtectedBase = None
+
class TestActualPython(GuardTestCase):
+
+ _old_mgr = _old_policy = _marker = []
+
+ def setUp(self):
+ pass
+
+ def tearDown( self ):
+ self._restorePolicyAndManager()
+
+ def _initPolicyAndManager(self, manager=None):
+ from AccessControl.SecurityManagement import get_ident
+ from AccessControl.SecurityManagement import _managers
+ from AccessControl.SecurityManagement import newSecurityManager
+ from AccessControl.SecurityManager import setSecurityPolicy
+ from AccessControl.ZopeSecurityPolicy import ZopeSecurityPolicy
+
+ class UnderprivilegedUser:
+ """ Anonymous USer for unit testing purposes.
+ """
+ def getId(self):
+ return 'Underprivileged User'
+
+ getUserName = getId
+
+ def allowed(self, object, object_roles=None):
+ return 0
+
+ def getRoles(self):
+ return ()
+
+ self._policy = ZopeSecurityPolicy()
+ self._old_policy = setSecurityPolicy(self._policy)
+
+ if manager is None:
+ thread_id = get_ident()
+ self._old_mgr = manager=_managers.get(thread_id, self._marker)
+ newSecurityManager(None, UnderprivilegedUser())
+ else:
+ self._old_mgr = self.setSecurityManager(manager)
+
+ def _restorePolicyAndManager(self):
+ from AccessControl.SecurityManagement import noSecurityManager
+ from AccessControl.SecurityManager import setSecurityPolicy
+
+ if self._old_mgr is not self._marker:
+ self.setSecurityManager(self._old_mgr)
+ else:
+ noSecurityManager()
+
+ if self._old_policy is not self._marker:
+ setSecurityPolicy(self._old_policy)
+
+ def _getProtectedBaseClass(self):
+
+ from AccessControl.SecurityInfo import ClassSecurityInfo
+ from ExtensionClass import Base
+ from Globals import InitializeClass
+
+ global _ProtectedBase
+ if _ProtectedBase is None:
+
+ class ProtectedBase(Base):
+ security = ClassSecurityInfo()
+
+ security.declarePrivate('private_method')
+ def private_method(self):
+ return 'private_method called'
+
+ InitializeClass(ProtectedBase)
+ _ProtectedBase = ProtectedBase
+
+ return _ProtectedBase
+
def testPython(self):
from RestrictedPython.tests import verify
@@ -404,6 +478,101 @@
untouched.sort()
self.fail("Unexercised wrappers: %r" % untouched)
+ def testPythonRealAC(self):
+ code, its_globals = self._compile("actual_python.py")
+ exec code in its_globals
+
+ def test_derived_class_normal(self):
+ from RestrictedPython.tests import verify
+
+ NORMAL_SCRIPT = """
+class Normal(ProtectedBase):
+ pass
+
+normal = Normal()
+print normal.private_method()
+"""
+ code, its_globals = self._compile_str(NORMAL_SCRIPT, 'normal_script')
+ its_globals['ProtectedBase'] = self._getProtectedBaseClass()
+ verify.verify(code)
+
+ self._initPolicyAndManager()
+
+ try:
+ exec code in its_globals
+ except Unauthorized:
+ pass
+ else:
+ self.fail("Didn't raise Unauthorized: \n%s" %
+ its_globals['_print']())
+
+ def test_derived_class_sneaky_en_suite(self):
+
+ # Disallow declaration of security-affecting names in classes
+ # defined in restricted code (compile-time check).
+ from RestrictedPython.tests import verify
+
+ SNEAKY_SCRIPT = """
+class Sneaky(ProtectedBase):
+ private_method__roles__ = None
+
+
+sneaky = Sneaky()
+print sneaky.private_method()
+"""
+ try:
+ code, its_globals = self._compile_str(SNEAKY_SCRIPT,
+ 'sneaky_script')
+ except SyntaxError:
+ pass
+ else:
+ self.fail("Didn't raise SyntaxError!")
+
+ def test_derived_sneaky_post_facto(self):
+
+ # Assignment to a class outside its suite fails at
+ # compile time with a SyntaxError.
+ from RestrictedPython.tests import verify
+
+ SNEAKY_SCRIPT = """
+class Sneaky(ProtectedBase):
+ pass
+
+Sneaky.private_method__roles__ = None
+
+sneaky = Sneaky()
+print sneaky.private_method()
+"""
+ try:
+ code, its_globals = self._compile_str(SNEAKY_SCRIPT, 'sneaky_script')
+ except SyntaxError:
+ pass
+ else:
+ self.fail("Didn't raise SyntaxError!")
+
+ def test_derived_sneaky_instance(self):
+
+ # Assignment of security-sensitive names to an instance
+ # fails at compile time with a SyntaxError.
+ from RestrictedPython.tests import verify
+
+ SNEAKY_SCRIPT = """
+class Sneaky(ProtectedBase):
+ pass
+
+sneaky = Sneaky()
+sneaky.private_method__roles__ = None
+print sneaky.private_method()
+"""
+ try:
+ code, its_globals = self._compile_str(SNEAKY_SCRIPT,
+ 'sneaky_script')
+ except SyntaxError:
+ pass
+ else:
+ self.fail("Didn't raise SyntaxError!")
+
+
def test_dict_access(self):
from RestrictedPython.tests import verify
Modified: Zope/trunk/lib/python/RestrictedPython/RestrictionMutator.py
===================================================================
--- Zope/trunk/lib/python/RestrictedPython/RestrictionMutator.py 2005-04-05 20:40:42 UTC (rev 29882)
+++ Zope/trunk/lib/python/RestrictedPython/RestrictionMutator.py 2005-04-05 20:58:31 UTC (rev 29883)
@@ -94,6 +94,9 @@
# Note: "_" *is* allowed.
self.error(node, '"%s" is an invalid variable name because'
' it starts with "_"' % name)
+ if name.endswith('__roles__'):
+ self.error(node, '"%s" is an invalid variable name because '
+ 'it ends with "__roles__".' % name)
if name == "printed":
self.error(node, '"printed" is a reserved name.')
@@ -109,6 +112,9 @@
# Note: "_" *is* allowed.
self.error(node, '"%s" is an invalid attribute name '
'because it starts with "_".' % name)
+ if name.endswith('__roles__'):
+ self.error(node, '"%s" is an invalid attribute name '
+ 'because it ends with "__roles__".' % name)
def prepBody(self, body):
"""Insert code for print at the beginning of the code suite."""
More information about the Zope-Checkins
mailing list