/*
* Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.org.apache.xerces.internal.utils;
import com.sun.org.apache.xerces.internal.impl.Constants;
import com.sun.org.apache.xerces.internal.util.SecurityManager;
import java.util.concurrent.CopyOnWriteArrayList;
import jdk.xml.internal.SecuritySupport;
import org.xml.sax.SAXException;
This class manages standard and implementation-specific limitations.
/**
* This class manages standard and implementation-specific limitations.
*
*/
public final class XMLSecurityManager {
States of the settings of a property, in the order: default value, value
set by FEATURE_SECURE_PROCESSING, jaxp.properties file, jaxp system
properties, and jaxp api properties
/**
* States of the settings of a property, in the order: default value, value
* set by FEATURE_SECURE_PROCESSING, jaxp.properties file, jaxp system
* properties, and jaxp api properties
*/
public static enum State {
//this order reflects the overriding order
DEFAULT("default"), FSP("FEATURE_SECURE_PROCESSING"),
JAXPDOTPROPERTIES("jaxp.properties"), SYSTEMPROPERTY("system property"),
APIPROPERTY("property");
final String literal;
State(String literal) {
this.literal = literal;
}
String literal() {
return literal;
}
}
Limits managed by the security manager
/**
* Limits managed by the security manager
*/
public static enum Limit {
ENTITY_EXPANSION_LIMIT("EntityExpansionLimit",
Constants.JDK_ENTITY_EXPANSION_LIMIT, Constants.SP_ENTITY_EXPANSION_LIMIT, 0, 64000),
MAX_OCCUR_NODE_LIMIT("MaxOccurLimit",
Constants.JDK_MAX_OCCUR_LIMIT, Constants.SP_MAX_OCCUR_LIMIT, 0, 5000),
ELEMENT_ATTRIBUTE_LIMIT("ElementAttributeLimit",
Constants.JDK_ELEMENT_ATTRIBUTE_LIMIT, Constants.SP_ELEMENT_ATTRIBUTE_LIMIT, 0, 10000),
TOTAL_ENTITY_SIZE_LIMIT("TotalEntitySizeLimit",
Constants.JDK_TOTAL_ENTITY_SIZE_LIMIT, Constants.SP_TOTAL_ENTITY_SIZE_LIMIT, 0, 50000000),
GENERAL_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit",
Constants.JDK_GENERAL_ENTITY_SIZE_LIMIT, Constants.SP_GENERAL_ENTITY_SIZE_LIMIT, 0, 0),
PARAMETER_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit",
Constants.JDK_PARAMETER_ENTITY_SIZE_LIMIT, Constants.SP_PARAMETER_ENTITY_SIZE_LIMIT, 0, 1000000),
MAX_ELEMENT_DEPTH_LIMIT("MaxElementDepthLimit",
Constants.JDK_MAX_ELEMENT_DEPTH, Constants.SP_MAX_ELEMENT_DEPTH, 0, 0),
MAX_NAME_LIMIT("MaxXMLNameLimit",
Constants.JDK_XML_NAME_LIMIT, Constants.SP_XML_NAME_LIMIT, 1000, 1000),
ENTITY_REPLACEMENT_LIMIT("EntityReplacementLimit",
Constants.JDK_ENTITY_REPLACEMENT_LIMIT, Constants.SP_ENTITY_REPLACEMENT_LIMIT, 0, 3000000);
final String key;
final String apiProperty;
final String systemProperty;
final int defaultValue;
final int secureValue;
Limit(String key, String apiProperty, String systemProperty, int value, int secureValue) {
this.key = key;
this.apiProperty = apiProperty;
this.systemProperty = systemProperty;
this.defaultValue = value;
this.secureValue = secureValue;
}
public boolean equalsAPIPropertyName(String propertyName) {
return (propertyName == null) ? false : apiProperty.equals(propertyName);
}
public boolean equalsSystemPropertyName(String propertyName) {
return (propertyName == null) ? false : systemProperty.equals(propertyName);
}
public String key() {
return key;
}
public String apiProperty() {
return apiProperty;
}
String systemProperty() {
return systemProperty;
}
public int defaultValue() {
return defaultValue;
}
int secureValue() {
return secureValue;
}
}
Map old property names with the new ones
/**
* Map old property names with the new ones
*/
public static enum NameMap {
ENTITY_EXPANSION_LIMIT(Constants.SP_ENTITY_EXPANSION_LIMIT, Constants.ENTITY_EXPANSION_LIMIT),
MAX_OCCUR_NODE_LIMIT(Constants.SP_MAX_OCCUR_LIMIT, Constants.MAX_OCCUR_LIMIT),
ELEMENT_ATTRIBUTE_LIMIT(Constants.SP_ELEMENT_ATTRIBUTE_LIMIT, Constants.ELEMENT_ATTRIBUTE_LIMIT);
final String newName;
final String oldName;
NameMap(String newName, String oldName) {
this.newName = newName;
this.oldName = oldName;
}
String getOldName(String newName) {
if (newName.equals(this.newName)) {
return oldName;
}
return null;
}
}
private static final int NO_LIMIT = 0;
Values of the properties
/**
* Values of the properties
*/
private final int[] values;
States of the settings for each property
/**
* States of the settings for each property
*/
private State[] states;
Flag indicating if secure processing is set
/**
* Flag indicating if secure processing is set
*/
boolean secureProcessing;
States that determine if properties are set explicitly
/**
* States that determine if properties are set explicitly
*/
private boolean[] isSet;
Index of the special entityCountInfo property
/**
* Index of the special entityCountInfo property
*/
private final int indexEntityCountInfo = 10000;
private String printEntityCountInfo = "";
Default constructor. Establishes default values for known security
vulnerabilities.
/**
* Default constructor. Establishes default values for known security
* vulnerabilities.
*/
public XMLSecurityManager() {
this(false);
}
Instantiate Security Manager in accordance with the status of
secure processing
Params: - secureProcessing –
/**
* Instantiate Security Manager in accordance with the status of
* secure processing
* @param secureProcessing
*/
public XMLSecurityManager(boolean secureProcessing) {
values = new int[Limit.values().length];
states = new State[Limit.values().length];
isSet = new boolean[Limit.values().length];
this.secureProcessing = secureProcessing;
for (Limit limit : Limit.values()) {
if (secureProcessing) {
values[limit.ordinal()] = limit.secureValue;
states[limit.ordinal()] = State.FSP;
} else {
values[limit.ordinal()] = limit.defaultValue();
states[limit.ordinal()] = State.DEFAULT;
}
}
//read system properties or jaxp.properties
readSystemProperties();
}
Setting FEATURE_SECURE_PROCESSING explicitly
/**
* Setting FEATURE_SECURE_PROCESSING explicitly
*/
public void setSecureProcessing(boolean secure) {
secureProcessing = secure;
for (Limit limit : Limit.values()) {
if (secure) {
setLimit(limit.ordinal(), State.FSP, limit.secureValue());
} else {
setLimit(limit.ordinal(), State.FSP, limit.defaultValue());
}
}
}
Return the state of secure processing
Returns: the state of secure processing
/**
* Return the state of secure processing
* @return the state of secure processing
*/
public boolean isSecureProcessing() {
return secureProcessing;
}
Set limit by property name and state
Params: - propertyName – property name
- state – the state of the property
- value – the value of the property
Returns: true if the property is managed by the security manager; false
if otherwise.
/**
* Set limit by property name and state
* @param propertyName property name
* @param state the state of the property
* @param value the value of the property
* @return true if the property is managed by the security manager; false
* if otherwise.
*/
public boolean setLimit(String propertyName, State state, Object value) {
int index = getIndex(propertyName);
if (index > -1) {
setLimit(index, state, value);
return true;
}
return false;
}
Set the value for a specific limit.
Params: - limit – the limit
- state – the state of the property
- value – the value of the property
/**
* Set the value for a specific limit.
*
* @param limit the limit
* @param state the state of the property
* @param value the value of the property
*/
public void setLimit(Limit limit, State state, int value) {
setLimit(limit.ordinal(), state, value);
}
Set the value of a property by its index
Params: - index – the index of the property
- state – the state of the property
- value – the value of the property
/**
* Set the value of a property by its index
*
* @param index the index of the property
* @param state the state of the property
* @param value the value of the property
*/
public void setLimit(int index, State state, Object value) {
if (index == indexEntityCountInfo) {
printEntityCountInfo = (String)value;
} else {
int temp;
if (Integer.class.isAssignableFrom(value.getClass())) {
temp = ((Integer)value).intValue();
} else {
temp = Integer.parseInt((String) value);
if (temp < 0) {
temp = 0;
}
}
setLimit(index, state, temp);
}
}
Set the value of a property by its index
Params: - index – the index of the property
- state – the state of the property
- value – the value of the property
/**
* Set the value of a property by its index
*
* @param index the index of the property
* @param state the state of the property
* @param value the value of the property
*/
public void setLimit(int index, State state, int value) {
if (index == indexEntityCountInfo) {
//if it's explicitly set, it's treated as yes no matter the value
printEntityCountInfo = Constants.JDK_YES;
} else {
//only update if it shall override
if (state.compareTo(states[index]) >= 0) {
values[index] = value;
states[index] = state;
isSet[index] = true;
}
}
}
Return the value of the specified property
Params: - propertyName – the property name
Returns: the value of the property as a string. If a property is managed
by this manager, its value shall not be null.
/**
* Return the value of the specified property
*
* @param propertyName the property name
* @return the value of the property as a string. If a property is managed
* by this manager, its value shall not be null.
*/
public String getLimitAsString(String propertyName) {
int index = getIndex(propertyName);
if (index > -1) {
return getLimitValueByIndex(index);
}
return null;
}
Return the value of the specified property
Params: - limit – the property
Returns: the value of the property
/**
* Return the value of the specified property
*
* @param limit the property
* @return the value of the property
*/
public int getLimit(Limit limit) {
return values[limit.ordinal()];
}
Return the value of a property by its ordinal
Params: - limit – the property
Returns: value of a property
/**
* Return the value of a property by its ordinal
*
* @param limit the property
* @return value of a property
*/
public String getLimitValueAsString(Limit limit) {
return Integer.toString(values[limit.ordinal()]);
}
Return the value of a property by its ordinal
Params: - index – the index of a property
Returns: limit of a property as a string
/**
* Return the value of a property by its ordinal
*
* @param index the index of a property
* @return limit of a property as a string
*/
public String getLimitValueByIndex(int index) {
if (index == indexEntityCountInfo) {
return printEntityCountInfo;
}
return Integer.toString(values[index]);
}
Return the state of the limit property
Params: - limit – the limit
Returns: the state of the limit property
/**
* Return the state of the limit property
*
* @param limit the limit
* @return the state of the limit property
*/
public State getState(Limit limit) {
return states[limit.ordinal()];
}
Return the state of the limit property
Params: - limit – the limit
Returns: the state of the limit property
/**
* Return the state of the limit property
*
* @param limit the limit
* @return the state of the limit property
*/
public String getStateLiteral(Limit limit) {
return states[limit.ordinal()].literal();
}
Get the index by property name
Params: - propertyName – property name
Returns: the index of the property if found; return -1 if not
/**
* Get the index by property name
*
* @param propertyName property name
* @return the index of the property if found; return -1 if not
*/
public int getIndex(String propertyName) {
for (Limit limit : Limit.values()) {
if (limit.equalsAPIPropertyName(propertyName)) {
//internally, ordinal is used as index
return limit.ordinal();
}
}
//special property to return entity count info
if (propertyName.equals(Constants.JDK_ENTITY_COUNT_INFO)) {
return indexEntityCountInfo;
}
return -1;
}
Check if there's no limit defined by the Security Manager
Params: - limit –
Returns:
/**
* Check if there's no limit defined by the Security Manager
* @param limit
* @return
*/
public boolean isNoLimit(int limit) {
return limit==NO_LIMIT;
}
Check if the size (length or count) of the specified limit property is
over the limit
Params: - limit – the type of the limit property
- entityName – the name of the entity
- size – the size (count or length) of the entity
Returns: true if the size is over the limit, false otherwise
/**
* Check if the size (length or count) of the specified limit property is
* over the limit
*
* @param limit the type of the limit property
* @param entityName the name of the entity
* @param size the size (count or length) of the entity
* @return true if the size is over the limit, false otherwise
*/
public boolean isOverLimit(Limit limit, String entityName, int size,
XMLLimitAnalyzer limitAnalyzer) {
return isOverLimit(limit.ordinal(), entityName, size, limitAnalyzer);
}
Check if the value (length or count) of the specified limit property is
over the limit
Params: - index – the index of the limit property
- entityName – the name of the entity
- size – the size (count or length) of the entity
Returns: true if the size is over the limit, false otherwise
/**
* Check if the value (length or count) of the specified limit property is
* over the limit
*
* @param index the index of the limit property
* @param entityName the name of the entity
* @param size the size (count or length) of the entity
* @return true if the size is over the limit, false otherwise
*/
public boolean isOverLimit(int index, String entityName, int size,
XMLLimitAnalyzer limitAnalyzer) {
if (values[index] == NO_LIMIT) {
return false;
}
if (size > values[index]) {
limitAnalyzer.addValue(index, entityName, size);
return true;
}
return false;
}
Check against cumulated value
Params: - limit – the type of the limit property
- size – the size (count or length) of the entity
Returns: true if the size is over the limit, false otherwise
/**
* Check against cumulated value
*
* @param limit the type of the limit property
* @param size the size (count or length) of the entity
* @return true if the size is over the limit, false otherwise
*/
public boolean isOverLimit(Limit limit, XMLLimitAnalyzer limitAnalyzer) {
return isOverLimit(limit.ordinal(), limitAnalyzer);
}
public boolean isOverLimit(int index, XMLLimitAnalyzer limitAnalyzer) {
if (values[index] == NO_LIMIT) {
return false;
}
if (index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() ||
index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() ||
index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() ||
index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal() ||
index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() ||
index == Limit.MAX_NAME_LIMIT.ordinal()
) {
return (limitAnalyzer.getTotalValue(index) > values[index]);
} else {
return (limitAnalyzer.getValue(index) > values[index]);
}
}
public void debugPrint(XMLLimitAnalyzer limitAnalyzer) {
if (printEntityCountInfo.equals(Constants.JDK_YES)) {
limitAnalyzer.debugPrint(this);
}
}
Indicate if a property is set explicitly
Params: - index –
Returns:
/**
* Indicate if a property is set explicitly
* @param index
* @return
*/
public boolean isSet(int index) {
return isSet[index];
}
public boolean printEntityCountInfo() {
return printEntityCountInfo.equals(Constants.JDK_YES);
}
Read from system properties, or those in jaxp.properties
/**
* Read from system properties, or those in jaxp.properties
*/
private void readSystemProperties() {
for (Limit limit : Limit.values()) {
if (!getSystemProperty(limit, limit.systemProperty())) {
//if system property is not found, try the older form if any
for (NameMap nameMap : NameMap.values()) {
String oldName = nameMap.getOldName(limit.systemProperty());
if (oldName != null) {
getSystemProperty(limit, oldName);
}
}
}
}
}
// Array list to store printed warnings for each SAX parser used
private static final CopyOnWriteArrayList<String> printedWarnings = new CopyOnWriteArrayList<>();
Prints out warnings if a parser does not support the specified feature/property.
Params: - parserClassName – the name of the parser class
- propertyName – the property name
- exception – the exception thrown by the parser
/**
* Prints out warnings if a parser does not support the specified feature/property.
*
* @param parserClassName the name of the parser class
* @param propertyName the property name
* @param exception the exception thrown by the parser
*/
public static void printWarning(String parserClassName, String propertyName, SAXException exception) {
String key = parserClassName+":"+propertyName;
if (printedWarnings.addIfAbsent(key)) {
System.err.println( "Warning: "+parserClassName+": "+exception.getMessage());
}
}
Read from system properties, or those in jaxp.properties
Params: - property – the type of the property
- sysPropertyName – the name of system property
/**
* Read from system properties, or those in jaxp.properties
*
* @param property the type of the property
* @param sysPropertyName the name of system property
*/
private boolean getSystemProperty(Limit limit, String sysPropertyName) {
try {
String value = SecuritySupport.getSystemProperty(sysPropertyName);
if (value != null && !value.equals("")) {
values[limit.ordinal()] = Integer.parseInt(value);
states[limit.ordinal()] = State.SYSTEMPROPERTY;
return true;
}
value = SecuritySupport.readJAXPProperty(sysPropertyName);
if (value != null && !value.equals("")) {
values[limit.ordinal()] = Integer.parseInt(value);
states[limit.ordinal()] = State.JAXPDOTPROPERTIES;
return true;
}
} catch (NumberFormatException e) {
//invalid setting
throw new NumberFormatException("Invalid setting for system property: " + limit.systemProperty());
}
return false;
}
Convert a value set through setProperty to XMLSecurityManager.
If the value is an instance of XMLSecurityManager, use it to override the default;
If the value is an old SecurityManager, convert to the new XMLSecurityManager.
Params: - value – user specified security manager
- securityManager – an instance of XMLSecurityManager
Returns: an instance of the new security manager XMLSecurityManager
/**
* Convert a value set through setProperty to XMLSecurityManager.
* If the value is an instance of XMLSecurityManager, use it to override the default;
* If the value is an old SecurityManager, convert to the new XMLSecurityManager.
*
* @param value user specified security manager
* @param securityManager an instance of XMLSecurityManager
* @return an instance of the new security manager XMLSecurityManager
*/
static public XMLSecurityManager convert(Object value, XMLSecurityManager securityManager) {
if (value == null) {
if (securityManager == null) {
securityManager = new XMLSecurityManager(true);
}
return securityManager;
}
if (XMLSecurityManager.class.isAssignableFrom(value.getClass())) {
return (XMLSecurityManager)value;
} else {
if (securityManager == null) {
securityManager = new XMLSecurityManager(true);
}
if (SecurityManager.class.isAssignableFrom(value.getClass())) {
SecurityManager origSM = (SecurityManager)value;
securityManager.setLimit(Limit.MAX_OCCUR_NODE_LIMIT, State.APIPROPERTY, origSM.getMaxOccurNodeLimit());
securityManager.setLimit(Limit.ENTITY_EXPANSION_LIMIT, State.APIPROPERTY, origSM.getEntityExpansionLimit());
securityManager.setLimit(Limit.ELEMENT_ATTRIBUTE_LIMIT, State.APIPROPERTY, origSM.getElementAttrLimit());
}
return securityManager;
}
}
}