diff -urN a/luci/site/luci/Products/PloneHotfix20100612/__init__.py b/luci/site/luci/Products/PloneHotfix20100612/__init__.py --- a/luci/site/luci/Products/PloneHotfix20100612/__init__.py 1969-12-31 19:00:00.000000000 -0500 +++ b/luci/site/luci/Products/PloneHotfix20100612/__init__.py 2011-02-25 11:06:52.000000000 -0500 @@ -0,0 +1,6 @@ +# CVE-2010-XXXX hotfix +import safe_html + +import logging +logging.getLogger('PloneHotfix20100612').info( + 'safe_html patched') diff -urN a/luci/site/luci/Products/PloneHotfix20100612/README.txt b/luci/site/luci/Products/PloneHotfix20100612/README.txt --- a/luci/site/luci/Products/PloneHotfix20100612/README.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/luci/site/luci/Products/PloneHotfix20100612/README.txt 2011-02-25 11:06:52.000000000 -0500 @@ -0,0 +1,61 @@ +Plone Hotfix 2010-06-12 +*********************** + +The fix +======= + +This hotfix causes the safe_html transform to be executed twice to close the +attack vector described in CVE-2010-2422 which allows injection of arbitrary +HTML that should normally be filtered. This implementation adds a small +performance penalty to the calculation of the safe_html transform, which +occurs on the first viewing of an object after creation, editing, or a server +restart. + +To exploit this flaw untrusted users must be able to author content on a +website. Thanks go to Alan Hoey for reporting this flaw. + +Installation for Plone 2.1 - 3.1 users +-------------------------------------- + +To install this hotfix add the directory PloneHotfix20100612 to your instance +products directory. If the hotfix has been successfully added you will see the +following message when starting the instance in foreground mode: + +2010-06-12 23:54:28 INFO PloneHotfix20100612 safe_html patched + +Installation for Plone 3.2 and 3.3 users +---------------------------------------- + +Although this hotfix will work with any version of Plone, users of Plone 3.2 +to Plone 3.3.5 should instead add the following to their buildout +configuration files and re-run buildout: + +[versions] +Products.PortalTransforms = 1.6.12 + +There will be no confirmation message on start-up, so the presence of the fix +can be verified by checking the version number of PortalTransforms in the Zope +Control Panel. + +Users of Plone 3.3.6 and above are unaffected. + +Information for Plone 4 users +----------------------------- + +No stable versions of Plone 4 are affected by this bug. Plone beta testers are +reminded that beta versions are not intended for use on production sites. + +Changelog +========= + +20100612-1 +---------- + +* Update readme with CVE number + [MatthewWilkes] + +20100612-0 +---------- + +* Initial implementation + [MatthewWilkes] \ No newline at end of file diff -urN a/luci/site/luci/Products/PloneHotfix20100612/safe_html.py b/luci/site/luci/Products/PloneHotfix20100612/safe_html.py --- a/luci/site/luci/Products/PloneHotfix20100612/safe_html.py 1969-12-31 19:00:00.000000000 -0500 +++ b/luci/site/luci/Products/PloneHotfix20100612/safe_html.py 2011-02-25 11:06:52.000000000 -0500 @@ -0,0 +1,11 @@ +from Products.PortalTransforms.transforms import safe_html + +convert_function = safe_html.SafeHTML.convert + +def convert(self, orig, data, **kwargs): + """Runs the standard safe_html transform twice.""" + halfway = convert_function(self, orig, data, **kwargs) + return convert_function(self, halfway.getData(), halfway, **kwargs) + +safe_html.SafeHTML.convert = convert + diff -urN a/luci/site/luci/Products/PloneHotfix20110720/__init__.py b/luci/site/luci/Products/PloneHotfix20110720/__init__.py --- a/luci/site/luci/Products/PloneHotfix20110720/__init__.py 1969-12-31 19:00:00.000000000 -0500 +++ b/luci/site/luci/Products/PloneHotfix20110720/__init__.py 2011-02-25 11:06:52.000000000 -0500 @@ -0,0 +1,98 @@ +from AccessControl.PermissionRole import PermissionRole +import Products.CMFPlone + +import logging +logger = logging.getLogger('PloneHotfix20110720') + + +class PatchTarget(object): + + klass = None + methods = () + + set_roles = ('Manager',) + kill_docstrings = False + + def __init__(self, klass, **kw): + self.klass = klass + self.__dict__.update(kw) + + +targets = [ + PatchTarget('Products.Archetypes.ReferenceEngine.ReferenceCatalog', + kill_docstrings = True), + PatchTarget('Products.Archetypes.Referenceable.Referenceable', + kill_docstrings = True), + PatchTarget('Products.ZCatalog.ZCatalog.ZCatalog', + kill_docstrings = True, methods=['getMetadataForRID', + 'getMetadataForUID', 'getIndexDataForRID', 'getIndexDataForUID', + 'getrid', 'resolve_path']), + PatchTarget('Products.PluggableAuthService.PluggableAuthService.PluggableAuthService', + methods='monkeys'), + PatchTarget('Products.PortalTransforms.TransformEngine.TransformTool', + methods=('convertTo', 'registerTransform', 'unregisterTransform'), + set_roles=()), + PatchTarget('Products.PortalTransforms.Transform.Transform', + methods=('convert',), + set_roles=()), + PatchTarget('Products.PlonePAS.plugins.user.UserManager', + set_roles=PermissionRole('Manage users', ('Manager',))), + PatchTarget('Products.PlonePAS.plugins.property.ZODBMutablePropertyProvider', + methods=('deleteUser', 'enumerateUsers')), + PatchTarget('Products.CMFPlone.PropertiesTool.PropertiesTool', + methods=('addPropertySheet',)), + PatchTarget('Products.CMFQuickInstallerTool.QuickInstallerTool.QuickInstallerTool', + methods=('isProductInstalled',)), + PatchTarget('plone.app.customerize.tool.ViewTemplateContainer'), + ] + + +def get_klass(dottedname): + parts = dottedname.split('.') + klassname = parts.pop() + modulename = '.'.join(parts) + try: + return getattr(__import__(modulename, [], [], [klassname]), klassname) + except: + raise ImportError + + +def initialize(context): + from Products.PloneHotfix20110720.publisher import install_patch + install_patch() + + for target in targets: + try: + klass = get_klass(target.klass) + except ImportError: + continue + + for k,v in klass.__dict__.items(): + if target.methods == 'monkeys': + modname = getattr(v, '__module__', '') or '' + if klass.__module__ == modname: + continue + elif target.methods and k not in target.methods: + continue + + if not callable(v): + continue + if k.startswith('_'): + continue + + roles = getattr(klass, '%s__roles__' % k, None) + if roles is None: + if target.kill_docstrings: + if getattr(v, '__doc__', None): + try: + del v.__doc__ + except: + pass + try: + del v.im_func.__doc__ + except: + pass + else: + setattr(klass, '%s__roles__' % k, target.set_roles) + + logger.info('Hotfix installed.') diff -urN a/luci/site/luci/Products/PloneHotfix20110720/publisher.py b/luci/site/luci/Products/PloneHotfix20110720/publisher.py --- a/luci/site/luci/Products/PloneHotfix20110720/publisher.py 1969-12-31 19:00:00.000000000 -0500 +++ b/luci/site/luci/Products/PloneHotfix20110720/publisher.py 2011-02-25 11:06:52.000000000 -0500 @@ -0,0 +1,138 @@ +import re +from Acquisition import aq_base +try: + from Acquisition.interfaces import IAcquirer + ZOPE_210 = False +except ImportError: + # Zope < 2.12 + IAcquirer = None + ZOPE_210 = True + +from zExceptions import Forbidden, NotFound + +from Products.PloneHotfix20110720 import logger + +PATCHED_ZOPE_VERSIONS = { + '10': 13, # 2.10.13 + '11': 8, # 2.11.8 + '12': 15, # 2.12.15 + '13': 3, # 2.13.3 + } + +patch = True +try: + import pkg_resources + zope_version = pkg_resources.get_distribution('Zope2').version +except: + # We can't determine the version, so we patch if possible + logger.info('Cannot determine Zope version, attempting to patch DefaultPublishTraverse') +else: + try: + major, minor, sub = zope_version.split('.') + except: + logger.info('Cannot determine Zope version, attempting to patch DefaultPublishTraverse') + else: + major,minor,sub = zope_version.split('.') + sub_match = re.match(r'^(\d+)', sub) + sub_version = sub_match and int(sub_match.groups()[0]) + sub_patched = PATCHED_ZOPE_VERSIONS.get(minor, None) + if sub_patched and sub_version >= sub_patched: + logger.info('Zope version %s already contains DefaultPublishTraverse fix'%zope_version) + patch = False + + +try: + from ZPublisher.BaseRequest import DefaultPublishTraverse + from ZPublisher.BaseRequest import typeCheck +except ImportError: + # patch not needed in Zope 2.9 + DefaultPublishTraverse = None +else: + from zope.component import queryMultiAdapter + from zope.interface import Interface + + def publishTraverse(self, request, name): + object = self.context + URL=request['URL'] + + if name[:1]=='_': + raise Forbidden("Object name begins with an underscore at: %s" % URL) + + + if hasattr(object,'__bobo_traverse__'): + try: + subobject=object.__bobo_traverse__(request, name) + if type(subobject) is type(()) and len(subobject) > 1: + # If traversal returns a tuple treat it as a sequence + # of objects traversed over in the background + request['PARENTS'][-1:] = list(subobject[:-1]) + object, subobject = subobject[-2:] + except (AttributeError, KeyError, NotFound), e: + # Try to find a view + subobject = queryMultiAdapter((object, request), Interface, name) + if subobject is not None: + # OFS.Application.__bobo_traverse__ calls + # REQUEST.RESPONSE.notFoundError which sets the HTTP + # status code to 404 + request.response.setStatus(200) + # We don't need to do the docstring security check + # for views, so lets skip it and return the object here. + if ZOPE_210 or (IAcquirer and IAcquirer.providedBy(subobject)): + subobject = subobject.__of__(object) + return subobject + # No view found. Reraise the error raised by __bobo_traverse__ + raise e + else: + # No __bobo_traverse__ + # Try with an unacquired attribute: + if hasattr(aq_base(object), name): + subobject = getattr(object, name) + else: + # We try to fall back to a view: + subobject = queryMultiAdapter((object, request), Interface, + name) + if subobject is not None: + if ZOPE_210 or (IAcquirer and IAcquirer.providedBy(subobject)): + subobject = subobject.__of__(object) + return subobject + + # And lastly, of there is no view, try acquired attributes, but + # only if there is no __bobo_traverse__: + try: + subobject=getattr(object, name) + # Again, clear any error status created by __bobo_traverse__ + # because we actually found something: + request.response.setStatus(200) + except AttributeError: + pass + + # Lastly we try with key access: + if subobject is None: + try: + subobject = object[name] + except TypeError: # unsubscriptable + raise KeyError(name) + + # Ensure that the object has a docstring. Objects that + # have an empty or missing docstring are not published. + doc = getattr(subobject, '__doc__', None) + if not doc: + raise Forbidden( + "The object at %s has an empty or missing " \ + "docstring. Objects must have a docstring to be " \ + "published." % URL + ) + + if not typeCheck(subobject): + raise Forbidden( + "The object at %s is not publishable." % URL + ) + + return subobject + + +def install_patch(): + # patch publishTraverse + if patch and DefaultPublishTraverse is not None: + logger.info('Patching ZPublisher.DefaultPublishTraverse.publishTraverse') + DefaultPublishTraverse.publishTraverse = publishTraverse diff -urN a/luci/site/luci/Products/PloneHotfix20110720/README.txt b/luci/site/luci/Products/PloneHotfix20110720/README.txt --- a/luci/site/luci/Products/PloneHotfix20110720/README.txt 1969-12-31 19:00:00.000000000 -0500 +++ b/luci/site/luci/Products/PloneHotfix20110720/README.txt 2011-02-25 11:06:52.000000000 -0500 @@ -0,0 +1,55 @@ +Plone Hotfix for CVE 2011-0720 +****************************** + +This is a critical security hotfix which should be applied to the following +versions of Plone: + +* Plone 4 <= 4.0.3 +* Plone 3 <= 3.3.5 +* Any version of Plone 2.5, 2.1, or 2.0 + +Additional information about the hotfix including frequently asked questions +is available at http://plone.org/products/plone/security/advisories/cve-2011-0720 + +This hotfix applies the following modifications to improve Plone security: + +* Applies security declarations to some methods that were missing them, in order + to address the vulnerability identified in `CVE 2011-0720`_. The vulnerability + discussed there affects Plone 2.5 and greater. +* Applies security declarations and removal of docstrings to some additional + methods that were identified by the Plone security team in an audit following + the identification of CVE 2011-0720. This includes some methods present in Plone + 2.0 and 2.1. +* If necessary, applies a patch to the ZPublisher to fix an issue with the checking + of whether traversed methods are publishable. This issue affects Plone 3.0 and + higher, and is also available in the following new Zope2 releases: + 2.10.13, 2.11.8, 2.12.15, 2.13.4 + +.. _`CVE 2011-0720`: http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2011-0720 + + +Installation +============ + +Installation instructions can be found at +http://plone.org/products/plone-hotfix/releases/CVE-2011-0720 + +Changelog +========= + +1.1 (2011-02-08) +---------------- + +- Try 2 ways to delete the docstring as we had one report of the way we were + using not working (thanks Andrew Mleczko for the report). + [davisagli] + +- Fix issue with application to some recent revisions of Zope 2.10. Thanks to + Ethan Jucovy for calling this to our attention. + [davisagli] + +1.0 (2011-02-08) +---------------- + +- Initial release + [Plone security team] diff -urN a/luci/site/Makefile b/luci/site/Makefile --- a/luci/site/Makefile 2009-05-21 09:35:23.000000000 -0400 +++ b/luci/site/Makefile 2011-02-25 11:06:52.000000000 -0500 @@ -69,6 +69,12 @@ ${INSTALL_DIR} -m 700 ${DESTDIR}/var/lib/luci/Products/ManagedSystem/skins ${INSTALL_FILE} -m 644 `find luci/Products/ManagedSystem/skins -maxdepth 1 -type f` ${DESTDIR}/var/lib/luci/Products/ManagedSystem/skins + ${INSTALL_DIR} -m 700 ${DESTDIR}/var/lib/luci/Products/PloneHotfix20100612 + ${INSTALL_FILE} -m 644 `find luci/Products/PloneHotfix20100612 -maxdepth 1 -type f | grep .py | grep -v \~ | grep -v \#` ${DESTDIR}/var/lib/luci/Products/PloneHotfix20100612 + + ${INSTALL_DIR} -m 700 ${DESTDIR}/var/lib/luci/Products/PloneHotfix20110720 + ${INSTALL_FILE} -m 644 `find luci/Products/PloneHotfix20110720 -maxdepth 1 -type f | grep .py | grep -v \~ | grep -v \#` ${DESTDIR}/var/lib/luci/Products/PloneHotfix20110720 + ${INSTALL_DIR} -m 700 ${DESTDIR}/var/lib/luci/var ${INSTALL_FILE} -m 600 luci/var/Data.fs ${DESTDIR}/var/lib/luci/var/Data.fs ${INSTALL_DIR} -m 700 ${DESTDIR}/var/lib/luci/var/pts