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