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