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.security.xml.SecureXMLFactoryProvider;
021    import com.liferay.portal.kernel.util.ClassLoaderUtil;
022    import com.liferay.portal.util.PropsValues;
023    
024    import javax.xml.XMLConstants;
025    import javax.xml.parsers.DocumentBuilderFactory;
026    import javax.xml.stream.XMLInputFactory;
027    
028    import org.apache.xerces.parsers.SAXParser;
029    
030    import org.xml.sax.XMLReader;
031    
032    /**
033     * @author Tomas Polesovsky
034     */
035    public class SecureXMLFactoryProviderImpl implements SecureXMLFactoryProvider {
036    
037            @Override
038            public DocumentBuilderFactory newDocumentBuilderFactory() {
039                    DocumentBuilderFactory documentBuilderFactory =
040                            DocumentBuilderFactory.newInstance();
041    
042                    if (!PropsValues.XML_SECURITY_ENABLED) {
043                            return documentBuilderFactory;
044                    }
045    
046                    try {
047                            documentBuilderFactory.setFeature(
048                                    XMLConstants.FEATURE_SECURE_PROCESSING, true);
049                    }
050                    catch (Exception e) {
051                            _log.error(
052                                    "Unable to initialize safe document builder factory to " +
053                                            "protect from XML Bomb attacks",
054                                    e);
055                    }
056    
057                    try {
058                            documentBuilderFactory.setFeature(
059                                    _FEATURES_DISALLOW_DOCTYPE_DECL, true);
060                    }
061                    catch (Exception e) {
062                            _log.error(
063                                    "Unable to initialize safe document builder factory to " +
064                                            "protect from XML Bomb attacks",
065                                    e);
066                    }
067    
068                    try {
069                            documentBuilderFactory.setExpandEntityReferences(false);
070                            documentBuilderFactory.setFeature(
071                                    _FEATURES_EXTERNAL_GENERAL_ENTITIES, false);
072                            documentBuilderFactory.setFeature(
073                                    _FEATURES_EXTERNAL_PARAMETER_ENTITIES, false);
074                    }
075                    catch (Exception e) {
076                            _log.error(
077                                    "Unable to initialize safe document builder factory to " +
078                                            "protect from XXE attacks",
079                                    e);
080                    }
081    
082                    return documentBuilderFactory;
083            }
084    
085            @Override
086            public XMLInputFactory newXMLInputFactory() {
087                    XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
088    
089                    if (!PropsValues.XML_SECURITY_ENABLED) {
090                            return xmlInputFactory;
091                    }
092    
093                    xmlInputFactory.setProperty(
094                            XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.FALSE);
095                    xmlInputFactory.setProperty(
096                            XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
097                    xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
098    
099                    return xmlInputFactory;
100            }
101    
102            @Override
103            public XMLReader newXMLReader() {
104                    Class<?> clazz = getClass();
105    
106                    ClassLoader classLoader = clazz.getClassLoader();
107    
108                    ClassLoader contextClassLoader =
109                            ClassLoaderUtil.getContextClassLoader();
110    
111                    XMLReader xmlReader = null;
112    
113                    try {
114                            if (classLoader != contextClassLoader) {
115                                    ClassLoaderUtil.setContextClassLoader(classLoader);
116                            }
117    
118                            xmlReader = new SAXParser();
119                    }
120                    catch (RuntimeException re) {
121                            throw new SystemException(re);
122                    }
123                    finally {
124                            if (classLoader != contextClassLoader) {
125                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
126                            }
127                    }
128    
129                    if (!PropsValues.XML_SECURITY_ENABLED) {
130                            return xmlReader;
131                    }
132    
133                    xmlReader = new StripDoctypeXMLReader(xmlReader);
134    
135                    try {
136                            xmlReader.setFeature(_FEATURES_DISALLOW_DOCTYPE_DECL, true);
137                    }
138                    catch (Exception e) {
139                            _log.error(
140                                    "Unable to initialize safe SAX parser to protect from XML " +
141                                            "Bomb attacks",
142                                    e);
143                    }
144    
145                    try {
146                            xmlReader.setFeature(_FEATURES_EXTERNAL_GENERAL_ENTITIES, false);
147                            xmlReader.setFeature(_FEATURES_EXTERNAL_PARAMETER_ENTITIES, false);
148                    }
149                    catch (Exception e) {
150                            _log.error(
151                                    "Unable to initialize safe SAX parser to protect from XXE " +
152                                            "attacks",
153                                    e);
154                    }
155    
156                    return xmlReader;
157            }
158    
159            private static final String _FEATURES_DISALLOW_DOCTYPE_DECL =
160                    "http://apache.org/xml/features/disallow-doctype-decl";
161    
162            private static final String _FEATURES_EXTERNAL_GENERAL_ENTITIES =
163                    "http://xml.org/sax/features/external-general-entities";
164    
165            private static final String _FEATURES_EXTERNAL_PARAMETER_ENTITIES =
166                    "http://xml.org/sax/features/external-parameter-entities";
167    
168            private static final Log _log = LogFactoryUtil.getLog(
169                    SecureXMLFactoryProviderImpl.class);
170    
171    }