001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.security.xml;
016    
017    import com.liferay.portal.kernel.exception.SystemException;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.util.ClassLoaderUtil;
021    import com.liferay.portal.util.PropsValues;
022    
023    import javax.xml.XMLConstants;
024    import javax.xml.parsers.DocumentBuilderFactory;
025    import javax.xml.stream.XMLInputFactory;
026    
027    import org.apache.xerces.parsers.SAXParser;
028    
029    import org.xml.sax.XMLReader;
030    
031    /**
032     * @author Tomas Polesovsky
033     */
034    public class SecureXMLFactoryProviderImpl implements SecureXMLFactoryProvider {
035    
036            @Override
037            public DocumentBuilderFactory newDocumentBuilderFactory() {
038                    DocumentBuilderFactory documentBuilderFactory =
039                            DocumentBuilderFactory.newInstance();
040    
041                    if (!PropsValues.XML_SECURITY_ENABLED) {
042                            return documentBuilderFactory;
043                    }
044    
045                    try {
046                            documentBuilderFactory.setFeature(
047                                    XMLConstants.FEATURE_SECURE_PROCESSING, true);
048                    }
049                    catch (Exception e) {
050                            _log.error(
051                                    "Unable to initialize safe document builder factory to " +
052                                            "protect from XML Bomb attacks",
053                                    e);
054                    }
055    
056                    try {
057                            documentBuilderFactory.setFeature(
058                                    _FEATURES_DISALLOW_DOCTYPE_DECL, true);
059                    }
060                    catch (Exception e) {
061                            _log.error(
062                                    "Unable to initialize safe document builder factory to " +
063                                            "protect from XML Bomb attacks",
064                                    e);
065                    }
066    
067                    try {
068                            documentBuilderFactory.setExpandEntityReferences(false);
069                            documentBuilderFactory.setFeature(
070                                    _FEATURES_EXTERNAL_GENERAL_ENTITIES, false);
071                            documentBuilderFactory.setFeature(
072                                    _FEATURES_EXTERNAL_PARAMETER_ENTITIES, false);
073                    }
074                    catch (Exception e) {
075                            _log.error(
076                                    "Unable to initialize safe document builder factory to " +
077                                            "protect from XXE attacks",
078                                    e);
079                    }
080    
081                    return documentBuilderFactory;
082            }
083    
084            @Override
085            public XMLInputFactory newXMLInputFactory() {
086                    XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
087    
088                    if (!PropsValues.XML_SECURITY_ENABLED) {
089                            return xmlInputFactory;
090                    }
091    
092                    xmlInputFactory.setProperty(
093                            XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.FALSE);
094                    xmlInputFactory.setProperty(
095                            XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
096                    xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
097    
098                    return xmlInputFactory;
099            }
100    
101            @Override
102            public XMLReader newXMLReader() {
103                    Class<?> clazz = getClass();
104    
105                    ClassLoader classLoader = clazz.getClassLoader();
106    
107                    ClassLoader contextClassLoader =
108                            ClassLoaderUtil.getContextClassLoader();
109    
110                    XMLReader xmlReader = null;
111    
112                    try {
113                            if (classLoader != contextClassLoader) {
114                                    ClassLoaderUtil.setContextClassLoader(classLoader);
115                            }
116    
117                            xmlReader = new SAXParser();
118                    }
119                    catch (RuntimeException re) {
120                            throw new SystemException(re);
121                    }
122                    finally {
123                            if (classLoader != contextClassLoader) {
124                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
125                            }
126                    }
127    
128                    if (!PropsValues.XML_SECURITY_ENABLED) {
129                            return xmlReader;
130                    }
131    
132                    xmlReader = new StripDoctypeXMLReader(xmlReader);
133    
134                    try {
135                            xmlReader.setFeature(_FEATURES_DISALLOW_DOCTYPE_DECL, true);
136                    }
137                    catch (Exception e) {
138                            _log.error(
139                                    "Unable to initialize safe SAX parser to protect from XML " +
140                                            "Bomb attacks",
141                                    e);
142                    }
143    
144                    try {
145                            xmlReader.setFeature(_FEATURES_EXTERNAL_GENERAL_ENTITIES, false);
146                            xmlReader.setFeature(_FEATURES_EXTERNAL_PARAMETER_ENTITIES, false);
147                    }
148                    catch (Exception e) {
149                            _log.error(
150                                    "Unable to initialize safe SAX parser to protect from XXE " +
151                                            "attacks",
152                                    e);
153                    }
154    
155                    return xmlReader;
156            }
157    
158            private static final String _FEATURES_DISALLOW_DOCTYPE_DECL =
159                    "http://apache.org/xml/features/disallow-doctype-decl";
160    
161            private static final String _FEATURES_EXTERNAL_GENERAL_ENTITIES =
162                    "http://xml.org/sax/features/external-general-entities";
163    
164            private static final String _FEATURES_EXTERNAL_PARAMETER_ENTITIES =
165                    "http://xml.org/sax/features/external-parameter-entities";
166    
167            private static final Log _log = LogFactoryUtil.getLog(
168                    SecureXMLFactoryProviderImpl.class);
169    
170    }