001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2015-2016 ForgeRock AS. 015 */ 016 017package org.forgerock.util.xml; 018 019import java.lang.reflect.Method; 020import javax.xml.XMLConstants; 021import javax.xml.parsers.DocumentBuilder; 022import javax.xml.parsers.DocumentBuilderFactory; 023import javax.xml.parsers.ParserConfigurationException; 024import javax.xml.parsers.SAXParser; 025import javax.xml.parsers.SAXParserFactory; 026import org.slf4j.Logger; 027import org.slf4j.LoggerFactory; 028import org.xml.sax.SAXException; 029 030/** 031 * Utility classes for handling XML. 032 */ 033public final class XMLUtils { 034 035 private static final Logger LOGGER = LoggerFactory.getLogger(XMLUtils.class); 036 private static final Object SECURITY_MANAGER; 037 private static final Integer ENTITY_EXP_LIMIT = 038 Integer.getInteger("org.forgerock.util.xml.entity.expansion.limit", 5000); 039 040 /** 041 * When Xerces is used for XML parsing, the only way to control entityExpansionLimit is to override the default 042 * SecurityManager. The following block will ensure that a Xerces SecurityManager is created and configured to have 043 * a less permissive entityExpansionLimit. 044 * In case Xerces is not used, but the JDK's XML parser implementation is leveraged, applications should enforce 045 * entity expansion limits by following the <a href="JAXP.java.net/1.4/JAXP-Compatibility.html#JAXP_security"> 046 * JAXP configuration guide</a>. 047 */ 048 static { 049 Object securityManager = null; 050 try { 051 Class<?> securityManagerClass = Class.forName("org.apache.xerces.util.SecurityManager"); 052 securityManager = securityManagerClass.newInstance(); 053 Method setEntityExpansionLimit = securityManagerClass.getMethod("setEntityExpansionLimit", int.class); 054 setEntityExpansionLimit.invoke(securityManager, ENTITY_EXP_LIMIT); 055 } catch (ClassNotFoundException ex) { 056 LOGGER.debug("Not using Xerces"); 057 } catch (Exception ex) { 058 LOGGER.debug("Unable to set expansion limit for Xerces, using default settings", ex); 059 securityManager = null; 060 } 061 SECURITY_MANAGER = securityManager; 062 } 063 064 private XMLUtils() { 065 // No impl. 066 } 067 068 /** 069 * Provides a secure DocumentBuilder implementation, which is protected against 070 * different types of entity expansion attacks and makes sure that only locally 071 * available DTDs can be referenced within the XML document. 072 * @param validating Whether the returned DocumentBuilder should validate input. 073 * @return A secure DocumentBuilder instance. 074 * @throws javax.xml.parsers.ParserConfigurationException In case xerces does not support one 075 * of the required features. 076 */ 077 public static DocumentBuilder getSafeDocumentBuilder(boolean validating) throws ParserConfigurationException { 078 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 079 dbf.setValidating(validating); 080 dbf.setNamespaceAware(true); 081 dbf.setXIncludeAware(false); 082 dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); 083 dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); 084 dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); 085 dbf.setExpandEntityReferences(false); 086 if (SECURITY_MANAGER != null) { 087 dbf.setAttribute("http://apache.org/xml/properties/security-manager", SECURITY_MANAGER); 088 } 089 try { 090 dbf.setAttribute("http://www.oracle.com/xml/jaxp/properties/entityExpansionLimit", ENTITY_EXP_LIMIT); 091 } catch (IllegalArgumentException ie) { } 092 DocumentBuilder db = dbf.newDocumentBuilder(); 093 db.setEntityResolver(new XMLHandler()); 094 return db; 095 } 096 097 /** 098 * Provides a secure SAXParser instance, which is protected against different 099 * types of entity expension, DoS attacks and makes sure that only locally 100 * available DTDs can be referenced within the XML document. 101 * @param validating Whether the returned DocumentBuilder should validate input. 102 * @return A secure SAXParser instance. 103 * @throws ParserConfigurationException In case Xerces does not support one of 104 * the required features. 105 * @throws SAXException In case Xerces does not support one of the required 106 * features. 107 */ 108 public static SAXParser getSafeSAXParser(boolean validating) throws ParserConfigurationException, SAXException { 109 SAXParserFactory saxFactory = SAXParserFactory.newInstance(); 110 saxFactory.setValidating(validating); 111 saxFactory.setNamespaceAware(true); 112 saxFactory.setXIncludeAware(false); 113 saxFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); 114 saxFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); 115 saxFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); 116 SAXParser sp = saxFactory.newSAXParser(); 117 if (SECURITY_MANAGER != null) { 118 sp.setProperty("http://apache.org/xml/properties/security-manager", SECURITY_MANAGER); 119 } 120 try { 121 sp.setProperty("http://www.oracle.com/xml/jaxp/properties/entityExpansionLimit", ENTITY_EXP_LIMIT); 122 } catch (Exception ex) { } 123 sp.getXMLReader().setEntityResolver(new XMLHandler()); 124 return sp; 125 } 126}