[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