Path: blob/master/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java
67862 views
/*1* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package jdk.xml.internal;252627import com.sun.org.apache.xerces.internal.util.SecurityManager;28import java.util.concurrent.CopyOnWriteArrayList;29import jdk.xml.internal.JdkProperty.State;30import jdk.xml.internal.JdkProperty.ImplPropMap;31import org.xml.sax.SAXException;3233/**34* This class manages standard and implementation-specific limitations.35*36*/37public final class XMLSecurityManager {3839/**40* Limits managed by the security manager41*/42@SuppressWarnings("deprecation")43public static enum Limit {44ENTITY_EXPANSION_LIMIT("EntityExpansionLimit", JdkConstants.JDK_ENTITY_EXPANSION_LIMIT,45JdkConstants.SP_ENTITY_EXPANSION_LIMIT, 0, 64000, Processor.PARSER),46MAX_OCCUR_NODE_LIMIT("MaxOccurLimit", JdkConstants.JDK_MAX_OCCUR_LIMIT,47JdkConstants.SP_MAX_OCCUR_LIMIT, 0, 5000, Processor.PARSER),48ELEMENT_ATTRIBUTE_LIMIT("ElementAttributeLimit", JdkConstants.JDK_ELEMENT_ATTRIBUTE_LIMIT,49JdkConstants.SP_ELEMENT_ATTRIBUTE_LIMIT, 0, 10000, Processor.PARSER),50TOTAL_ENTITY_SIZE_LIMIT("TotalEntitySizeLimit", JdkConstants.JDK_TOTAL_ENTITY_SIZE_LIMIT,51JdkConstants.SP_TOTAL_ENTITY_SIZE_LIMIT, 0, 50000000, Processor.PARSER),52GENERAL_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit", JdkConstants.JDK_GENERAL_ENTITY_SIZE_LIMIT,53JdkConstants.SP_GENERAL_ENTITY_SIZE_LIMIT, 0, 0, Processor.PARSER),54PARAMETER_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit", JdkConstants.JDK_PARAMETER_ENTITY_SIZE_LIMIT,55JdkConstants.SP_PARAMETER_ENTITY_SIZE_LIMIT, 0, 1000000, Processor.PARSER),56MAX_ELEMENT_DEPTH_LIMIT("MaxElementDepthLimit", JdkConstants.JDK_MAX_ELEMENT_DEPTH,57JdkConstants.SP_MAX_ELEMENT_DEPTH, 0, 0, Processor.PARSER),58MAX_NAME_LIMIT("MaxXMLNameLimit", JdkConstants.JDK_XML_NAME_LIMIT,59JdkConstants.SP_XML_NAME_LIMIT, 1000, 1000, Processor.PARSER),60ENTITY_REPLACEMENT_LIMIT("EntityReplacementLimit", JdkConstants.JDK_ENTITY_REPLACEMENT_LIMIT,61JdkConstants.SP_ENTITY_REPLACEMENT_LIMIT, 0, 3000000, Processor.PARSER),62XPATH_GROUP_LIMIT("XPathGroupLimit", JdkConstants.XPATH_GROUP_LIMIT,63JdkConstants.XPATH_GROUP_LIMIT, 10, 10, Processor.XPATH),64XPATH_OP_LIMIT("XPathExprOpLimit", JdkConstants.XPATH_OP_LIMIT,65JdkConstants.XPATH_OP_LIMIT, 100, 100, Processor.XPATH),66XPATH_TOTALOP_LIMIT("XPathTotalOpLimit", JdkConstants.XPATH_TOTALOP_LIMIT,67JdkConstants.XPATH_TOTALOP_LIMIT, 10000, 10000, Processor.XPATH)68;6970final String key;71final String apiProperty;72final String systemProperty;73final int defaultValue;74final int secureValue;75final Processor processor;7677Limit(String key, String apiProperty, String systemProperty, int value,78int secureValue, Processor processor) {79this.key = key;80this.apiProperty = apiProperty;81this.systemProperty = systemProperty;82this.defaultValue = value;83this.secureValue = secureValue;84this.processor = processor;85}8687/**88* Checks whether the specified name is a limit. Checks both the89* property and System Property which is now the new property name.90*91* @param name the specified name92* @return true if there is a match, false otherwise93*/94public boolean is(String name) {95// current spec: new property name == systemProperty96return (systemProperty != null && systemProperty.equals(name)) ||97// current spec: apiProperty is legacy98(apiProperty.equals(name));99}100101/**102* Returns the state of a property name. By the specification as of JDK 17,103* the "jdk.xml." prefixed System property name is also the current API104* name. The URI-based qName is legacy.105*106* @param name the property name107* @return the state of the property name, null if no match108*/109public State getState(String name) {110if (systemProperty != null && systemProperty.equals(name)) {111return State.APIPROPERTY;112} else if (apiProperty.equals(name)) {113//the URI-style qName is legacy114return State.LEGACY_APIPROPERTY;115}116return null;117}118119public String key() {120return key;121}122123public String apiProperty() {124return apiProperty;125}126127public String systemProperty() {128return systemProperty;129}130131public int defaultValue() {132return defaultValue;133}134135public boolean isSupported(Processor p) {136return processor == p;137}138139int secureValue() {140return secureValue;141}142}143144/**145* Map old property names with the new ones146*/147public static enum NameMap {148149ENTITY_EXPANSION_LIMIT(JdkConstants.SP_ENTITY_EXPANSION_LIMIT, JdkConstants.ENTITY_EXPANSION_LIMIT),150MAX_OCCUR_NODE_LIMIT(JdkConstants.SP_MAX_OCCUR_LIMIT, JdkConstants.MAX_OCCUR_LIMIT),151ELEMENT_ATTRIBUTE_LIMIT(JdkConstants.SP_ELEMENT_ATTRIBUTE_LIMIT, JdkConstants.ELEMENT_ATTRIBUTE_LIMIT);152final String newName;153final String oldName;154155NameMap(String newName, String oldName) {156this.newName = newName;157this.oldName = oldName;158}159160String getOldName(String newName) {161if (newName.equals(this.newName)) {162return oldName;163}164return null;165}166}167168/**169* Supported processors170*/171public static enum Processor {172PARSER,173XPATH,174}175176private static final int NO_LIMIT = 0;177178/**179* Values of the properties180*/181private final int[] values;182183/**184* States of the settings for each property185*/186private State[] states;187188/**189* Flag indicating if secure processing is set190*/191boolean secureProcessing;192193/**194* States that determine if properties are set explicitly195*/196private boolean[] isSet;197198199/**200* Index of the special entityCountInfo property201*/202private final int indexEntityCountInfo = 10000;203private String printEntityCountInfo = "";204205/**206* Default constructor. Establishes default values for known security207* vulnerabilities.208*/209public XMLSecurityManager() {210this(false);211}212213/**214* Instantiate Security Manager in accordance with the status of215* secure processing216* @param secureProcessing217*/218public XMLSecurityManager(boolean secureProcessing) {219values = new int[Limit.values().length];220states = new State[Limit.values().length];221isSet = new boolean[Limit.values().length];222this.secureProcessing = secureProcessing;223for (Limit limit : Limit.values()) {224if (secureProcessing) {225values[limit.ordinal()] = limit.secureValue;226states[limit.ordinal()] = State.FSP;227} else {228values[limit.ordinal()] = limit.defaultValue();229states[limit.ordinal()] = State.DEFAULT;230}231}232//read system properties or jaxp.properties233readSystemProperties();234}235236/**237* Setting FEATURE_SECURE_PROCESSING explicitly238*/239public void setSecureProcessing(boolean secure) {240secureProcessing = secure;241for (Limit limit : Limit.values()) {242if (secure) {243setLimit(limit.ordinal(), State.FSP, limit.secureValue());244} else {245setLimit(limit.ordinal(), State.FSP, limit.defaultValue());246}247}248}249250/**251* Return the state of secure processing252* @return the state of secure processing253*/254public boolean isSecureProcessing() {255return secureProcessing;256}257258/**259* Finds a limit's new name with the given property name.260* @param propertyName the property name specified261* @return the limit's new name if found, null otherwise262*/263public String find(String propertyName) {264for (Limit limit : Limit.values()) {265if (limit.is(propertyName)) {266// current spec: new property name == systemProperty267return limit.systemProperty();268}269}270//ENTITYCOUNT's new name is qName271if (ImplPropMap.ENTITYCOUNT.is(propertyName)) {272return ImplPropMap.ENTITYCOUNT.qName();273}274return null;275}276277/**278* Set limit by property name and state279* @param propertyName property name280* @param state the state of the property281* @param value the value of the property282* @return true if the property is managed by the security manager; false283* if otherwise.284*/285public boolean setLimit(String propertyName, State state, Object value) {286int index = getIndex(propertyName);287if (index > -1) {288State pState = state;289if (index != indexEntityCountInfo && state == State.APIPROPERTY) {290pState = (Limit.values()[index]).getState(propertyName);291}292setLimit(index, pState, value);293return true;294}295return false;296}297298/**299* Set the value for a specific limit.300*301* @param limit the limit302* @param state the state of the property303* @param value the value of the property304*/305public void setLimit(Limit limit, State state, int value) {306setLimit(limit.ordinal(), state, value);307}308309/**310* Set the value of a property by its index311*312* @param index the index of the property313* @param state the state of the property314* @param value the value of the property315*/316public void setLimit(int index, State state, Object value) {317if (index == indexEntityCountInfo) {318printEntityCountInfo = (String)value;319} else {320int temp;321if (value instanceof Integer) {322temp = (Integer)value;323} else {324temp = Integer.parseInt((String) value);325if (temp < 0) {326temp = 0;327}328}329setLimit(index, state, temp);330}331}332333/**334* Set the value of a property by its index335*336* @param index the index of the property337* @param state the state of the property338* @param value the value of the property339*/340public void setLimit(int index, State state, int value) {341if (index == indexEntityCountInfo) {342//if it's explicitly set, it's treated as yes no matter the value343printEntityCountInfo = JdkConstants.JDK_YES;344} else {345//only update if it shall override346if (state.compareTo(states[index]) >= 0) {347values[index] = value;348states[index] = state;349isSet[index] = true;350}351}352}353354/**355* Return the value of the specified property356*357* @param propertyName the property name358* @return the value of the property as a string. If a property is managed359* by this manager, its value shall not be null.360*/361public String getLimitAsString(String propertyName) {362int index = getIndex(propertyName);363if (index > -1) {364return getLimitValueByIndex(index);365}366367return null;368}369/**370* Return the value of the specified property371*372* @param limit the property373* @return the value of the property374*/375public int getLimit(Limit limit) {376return values[limit.ordinal()];377}378379/**380* Return the value of a property by its ordinal381*382* @param limit the property383* @return value of a property384*/385public String getLimitValueAsString(Limit limit) {386return Integer.toString(values[limit.ordinal()]);387}388389/**390* Return the value of a property by its ordinal391*392* @param index the index of a property393* @return limit of a property as a string394*/395public String getLimitValueByIndex(int index) {396if (index == indexEntityCountInfo) {397return printEntityCountInfo;398}399400return Integer.toString(values[index]);401}402403/**404* Return the state of the limit property405*406* @param limit the limit407* @return the state of the limit property408*/409public State getState(Limit limit) {410return states[limit.ordinal()];411}412413/**414* Return the state of the limit property415*416* @param limit the limit417* @return the state of the limit property418*/419public String getStateLiteral(Limit limit) {420return states[limit.ordinal()].literal();421}422423/**424* Get the index by property name425*426* @param propertyName property name427* @return the index of the property if found; return -1 if not428*/429public int getIndex(String propertyName) {430for (Limit limit : Limit.values()) {431// see JDK-8265248, accept both the URL and jdk.xml as prefix432if (limit.is(propertyName)) {433//internally, ordinal is used as index434return limit.ordinal();435}436}437//special property to return entity count info438if (ImplPropMap.ENTITYCOUNT.is(propertyName)) {439return indexEntityCountInfo;440}441return -1;442}443444/**445* Check if there's no limit defined by the Security Manager446* @param limit447* @return448*/449public boolean isNoLimit(int limit) {450return limit == NO_LIMIT;451}452/**453* Check if the size (length or count) of the specified limit property is454* over the limit455*456* @param limit the type of the limit property457* @param entityName the name of the entity458* @param size the size (count or length) of the entity459* @return true if the size is over the limit, false otherwise460*/461public boolean isOverLimit(Limit limit, String entityName, int size,462XMLLimitAnalyzer limitAnalyzer) {463return isOverLimit(limit.ordinal(), entityName, size, limitAnalyzer);464}465466/**467* Check if the value (length or count) of the specified limit property is468* over the limit469*470* @param index the index of the limit property471* @param entityName the name of the entity472* @param size the size (count or length) of the entity473* @return true if the size is over the limit, false otherwise474*/475public boolean isOverLimit(int index, String entityName, int size,476XMLLimitAnalyzer limitAnalyzer) {477if (values[index] == NO_LIMIT) {478return false;479}480if (size > values[index]) {481limitAnalyzer.addValue(index, entityName, size);482return true;483}484return false;485}486487/**488* Check against cumulated value489*490* @param limit the type of the limit property491* @param size the size (count or length) of the entity492* @return true if the size is over the limit, false otherwise493*/494public boolean isOverLimit(Limit limit, XMLLimitAnalyzer limitAnalyzer) {495return isOverLimit(limit.ordinal(), limitAnalyzer);496}497498public boolean isOverLimit(int index, XMLLimitAnalyzer limitAnalyzer) {499if (values[index] == NO_LIMIT) {500return false;501}502503if (index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() ||504index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() ||505index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() ||506index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal() ||507index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() ||508index == Limit.MAX_NAME_LIMIT.ordinal()509) {510return (limitAnalyzer.getTotalValue(index) > values[index]);511} else {512return (limitAnalyzer.getValue(index) > values[index]);513}514}515516public void debugPrint(XMLLimitAnalyzer limitAnalyzer) {517if (printEntityCountInfo.equals(JdkConstants.JDK_YES)) {518limitAnalyzer.debugPrint(this);519}520}521522523/**524* Indicate if a property is set explicitly525* @param index526* @return527*/528public boolean isSet(int index) {529return isSet[index];530}531532public boolean printEntityCountInfo() {533return printEntityCountInfo.equals(JdkConstants.JDK_YES);534}535536/**537* Read from system properties, or those in jaxp.properties538*/539private void readSystemProperties() {540541for (Limit limit : Limit.values()) {542if (!getSystemProperty(limit, limit.systemProperty())) {543//if system property is not found, try the older form if any544for (NameMap nameMap : NameMap.values()) {545String oldName = nameMap.getOldName(limit.systemProperty());546if (oldName != null) {547getSystemProperty(limit, oldName);548}549}550}551}552553}554555// Array list to store printed warnings for each SAX parser used556private static final CopyOnWriteArrayList<String> printedWarnings = new CopyOnWriteArrayList<>();557558/**559* Prints out warnings if a parser does not support the specified feature/property.560*561* @param parserClassName the name of the parser class562* @param propertyName the property name563* @param exception the exception thrown by the parser564*/565public static void printWarning(String parserClassName, String propertyName, SAXException exception) {566String key = parserClassName+":"+propertyName;567if (printedWarnings.addIfAbsent(key)) {568System.err.println( "Warning: "+parserClassName+": "+exception.getMessage());569}570}571572/**573* Read from system properties, or those in jaxp.properties574*575* @param property the type of the property576* @param sysPropertyName the name of system property577*/578private boolean getSystemProperty(Limit limit, String sysPropertyName) {579try {580String value = SecuritySupport.getSystemProperty(sysPropertyName);581if (value != null && !value.equals("")) {582values[limit.ordinal()] = Integer.parseInt(value);583states[limit.ordinal()] = State.SYSTEMPROPERTY;584return true;585}586587value = SecuritySupport.readJAXPProperty(sysPropertyName);588if (value != null && !value.equals("")) {589values[limit.ordinal()] = Integer.parseInt(value);590states[limit.ordinal()] = State.JAXPDOTPROPERTIES;591return true;592}593} catch (NumberFormatException e) {594//invalid setting595throw new NumberFormatException("Invalid setting for system property: " + limit.systemProperty());596}597return false;598}599600601/**602* Convert a value set through setProperty to XMLSecurityManager.603* If the value is an instance of XMLSecurityManager, use it to override the default;604* If the value is an old SecurityManager, convert to the new XMLSecurityManager.605*606* @param value user specified security manager607* @param securityManager an instance of XMLSecurityManager608* @return an instance of the new security manager XMLSecurityManager609*/610public static XMLSecurityManager convert(Object value, XMLSecurityManager securityManager) {611if (value == null) {612if (securityManager == null) {613securityManager = new XMLSecurityManager(true);614}615return securityManager;616}617if (value instanceof XMLSecurityManager) {618return (XMLSecurityManager)value;619} else {620if (securityManager == null) {621securityManager = new XMLSecurityManager(true);622}623if (value instanceof SecurityManager) {624SecurityManager origSM = (SecurityManager)value;625securityManager.setLimit(Limit.MAX_OCCUR_NODE_LIMIT, State.APIPROPERTY, origSM.getMaxOccurNodeLimit());626securityManager.setLimit(Limit.ENTITY_EXPANSION_LIMIT, State.APIPROPERTY, origSM.getEntityExpansionLimit());627securityManager.setLimit(Limit.ELEMENT_ATTRIBUTE_LIMIT, State.APIPROPERTY, origSM.getElementAttrLimit());628}629return securityManager;630}631}632}633634635