1 /*
   2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.org.apache.xalan.internal.utils;
  27 
  28 import com.sun.org.apache.xalan.internal.XalanConstants;
  29 import java.util.concurrent.CopyOnWriteArrayList;
  30 import org.xml.sax.SAXException;
  31 
  32 
  33 /**
  34  * This class is not the same as that in Xerces. It is used to manage the
  35  * state of corresponding Xerces properties and pass the values over to
  36  * the Xerces Security Manager.
  37  *
  38  * @author Joe Wang Oracle Corp.
  39  *
  40  */
  41 public final class XMLSecurityManager {
  42 
  43     /**
  44      * States of the settings of a property, in the order: default value, value
  45      * set by FEATURE_SECURE_PROCESSING, jaxp.properties file, jaxp system
  46      * properties, and jaxp api properties
  47      */
  48     public static enum State {
  49         //this order reflects the overriding order
  50 
  51         DEFAULT("default"), FSP("FEATURE_SECURE_PROCESSING"),
  52         JAXPDOTPROPERTIES("jaxp.properties"), SYSTEMPROPERTY("system property"),
  53         APIPROPERTY("property");
  54 
  55         final String literal;
  56         State(String literal) {
  57             this.literal = literal;
  58         }
  59 
  60         String literal() {
  61             return literal;
  62         }
  63     }
  64 
  65     /**
  66      * Limits managed by the security manager
  67      */
  68     public static enum Limit {
  69 
  70         ENTITY_EXPANSION_LIMIT("EntityExpansionLimit", XalanConstants.JDK_ENTITY_EXPANSION_LIMIT,
  71                 XalanConstants.SP_ENTITY_EXPANSION_LIMIT, 0, 64000),
  72         MAX_OCCUR_NODE_LIMIT("MaxOccurLimit", XalanConstants.JDK_MAX_OCCUR_LIMIT,
  73                 XalanConstants.SP_MAX_OCCUR_LIMIT, 0, 5000),
  74         ELEMENT_ATTRIBUTE_LIMIT("ElementAttributeLimit", XalanConstants.JDK_ELEMENT_ATTRIBUTE_LIMIT,
  75                 XalanConstants.SP_ELEMENT_ATTRIBUTE_LIMIT, 0, 10000),
  76         TOTAL_ENTITY_SIZE_LIMIT("TotalEntitySizeLimit", XalanConstants.JDK_TOTAL_ENTITY_SIZE_LIMIT,
  77                 XalanConstants.SP_TOTAL_ENTITY_SIZE_LIMIT, 0, 50000000),
  78         GENERAL_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit", XalanConstants.JDK_GENERAL_ENTITY_SIZE_LIMIT,
  79                 XalanConstants.SP_GENERAL_ENTITY_SIZE_LIMIT, 0, 0),
  80         PARAMETER_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit", XalanConstants.JDK_PARAMETER_ENTITY_SIZE_LIMIT,
  81                 XalanConstants.SP_PARAMETER_ENTITY_SIZE_LIMIT, 0, 1000000),
  82         MAX_ELEMENT_DEPTH_LIMIT("MaxElementDepthLimit", XalanConstants.JDK_MAX_ELEMENT_DEPTH,
  83                 XalanConstants.SP_MAX_ELEMENT_DEPTH, 0, 0),
  84         MAX_NAME_LIMIT("MaxXMLNameLimit", XalanConstants.JDK_XML_NAME_LIMIT,
  85                 XalanConstants.SP_XML_NAME_LIMIT, 1000, 1000);
  86 
  87         final String key;
  88         final String apiProperty;
  89         final String systemProperty;
  90         final int defaultValue;
  91         final int secureValue;
  92 
  93         Limit(String key, String apiProperty, String systemProperty, int value, int secureValue) {
  94             this.key = key;
  95             this.apiProperty = apiProperty;
  96             this.systemProperty = systemProperty;
  97             this.defaultValue = value;
  98             this.secureValue = secureValue;
  99         }
 100 
 101         public boolean equalsAPIPropertyName(String propertyName) {
 102             return (propertyName == null) ? false : apiProperty.equals(propertyName);
 103         }
 104 
 105         public boolean equalsSystemPropertyName(String propertyName) {
 106             return (propertyName == null) ? false : systemProperty.equals(propertyName);
 107         }
 108 
 109         public String key() {
 110             return key;
 111         }
 112 
 113         public String apiProperty() {
 114             return apiProperty;
 115         }
 116 
 117         String systemProperty() {
 118             return systemProperty;
 119         }
 120 
 121         public int defaultValue() {
 122             return defaultValue;
 123         }
 124 
 125         int secureValue() {
 126             return secureValue;
 127         }
 128     }
 129 
 130     /**
 131      * Map old property names with the new ones
 132      */
 133     public static enum NameMap {
 134 
 135         ENTITY_EXPANSION_LIMIT(XalanConstants.SP_ENTITY_EXPANSION_LIMIT,
 136                 XalanConstants.ENTITY_EXPANSION_LIMIT),
 137         MAX_OCCUR_NODE_LIMIT(XalanConstants.SP_MAX_OCCUR_LIMIT,
 138                 XalanConstants.MAX_OCCUR_LIMIT),
 139         ELEMENT_ATTRIBUTE_LIMIT(XalanConstants.SP_ELEMENT_ATTRIBUTE_LIMIT,
 140                 XalanConstants.ELEMENT_ATTRIBUTE_LIMIT);
 141         final String newName;
 142         final String oldName;
 143 
 144         NameMap(String newName, String oldName) {
 145             this.newName = newName;
 146             this.oldName = oldName;
 147         }
 148 
 149         String getOldName(String newName) {
 150             if (newName.equals(this.newName)) {
 151                 return oldName;
 152             }
 153             return null;
 154         }
 155     }
 156     /**
 157      * Values of the properties
 158      */
 159     private final int[] values;
 160     /**
 161      * States of the settings for each property
 162      */
 163     private State[] states;
 164     /**
 165      * States that determine if properties are set explicitly
 166      */
 167     private boolean[] isSet;
 168 
 169 
 170     /**
 171      * Index of the special entityCountInfo property
 172      */
 173     private final int indexEntityCountInfo = 10000;
 174     private String printEntityCountInfo = "";
 175 
 176     /**
 177      * Default constructor. Establishes default values for known security
 178      * vulnerabilities.
 179      */
 180     public XMLSecurityManager() {
 181         this(false);
 182     }
 183 
 184     /**
 185      * Instantiate Security Manager in accordance with the status of
 186      * secure processing
 187      * @param secureProcessing
 188      */
 189     public XMLSecurityManager(boolean secureProcessing) {
 190         values = new int[Limit.values().length];
 191         states = new State[Limit.values().length];
 192         isSet = new boolean[Limit.values().length];
 193         for (Limit limit : Limit.values()) {
 194             if (secureProcessing) {
 195                 values[limit.ordinal()] = limit.secureValue();
 196                 states[limit.ordinal()] = State.FSP;
 197             } else {
 198                 values[limit.ordinal()] = limit.defaultValue();
 199                 states[limit.ordinal()] = State.DEFAULT;
 200             }
 201         }
 202         //read system properties or jaxp.properties
 203         readSystemProperties();
 204     }
 205 
 206     /**
 207      * Setting FEATURE_SECURE_PROCESSING explicitly
 208      */
 209     public void setSecureProcessing(boolean secure) {
 210         for (Limit limit : Limit.values()) {
 211             if (secure) {
 212                 setLimit(limit.ordinal(), State.FSP, limit.secureValue());
 213             } else {
 214                 setLimit(limit.ordinal(), State.FSP, limit.defaultValue());
 215             }
 216         }
 217     }
 218 
 219     /**
 220      * Set limit by property name and state
 221      * @param propertyName property name
 222      * @param state the state of the property
 223      * @param value the value of the property
 224      * @return true if the property is managed by the security manager; false
 225      *              if otherwise.
 226      */
 227     public boolean setLimit(String propertyName, State state, Object value) {
 228         int index = getIndex(propertyName);
 229         if (index > -1) {
 230             setLimit(index, state, value);
 231             return true;
 232         }
 233         return false;
 234     }
 235 
 236     /**
 237      * Set the value for a specific limit.
 238      *
 239      * @param limit the limit
 240      * @param state the state of the property
 241      * @param value the value of the property
 242      */
 243     public void setLimit(Limit limit, State state, int value) {
 244         setLimit(limit.ordinal(), state, value);
 245     }
 246 
 247     /**
 248      * Set the value of a property by its index
 249      *
 250      * @param index the index of the property
 251      * @param state the state of the property
 252      * @param value the value of the property
 253      */
 254     public void setLimit(int index, State state, Object value) {
 255         if (index == indexEntityCountInfo) {
 256             //if it's explicitly set, it's treated as yes no matter the value
 257             printEntityCountInfo = (String)value;
 258         } else {
 259             int temp = 0;
 260             try {
 261                 temp = Integer.parseInt((String) value);
 262                 if (temp < 0) {
 263                     temp = 0;
 264                 }
 265             } catch (NumberFormatException e) {}
 266             setLimit(index, state, temp);        }
 267     }
 268 
 269     /**
 270      * Set the value of a property by its index
 271      *
 272      * @param index the index of the property
 273      * @param state the state of the property
 274      * @param value the value of the property
 275      */
 276     public void setLimit(int index, State state, int value) {
 277         if (index == indexEntityCountInfo) {
 278             //if it's explicitly set, it's treated as yes no matter the value
 279             printEntityCountInfo = XalanConstants.JDK_YES;
 280         } else {
 281             //only update if it shall override
 282             if (state.compareTo(states[index]) >= 0) {
 283                 values[index] = value;
 284                 states[index] = state;
 285                 isSet[index] = true;
 286             }
 287         }
 288     }
 289 
 290 
 291     /**
 292      * Return the value of the specified property.
 293      *
 294      * @param propertyName the property name
 295      * @return the value of the property as a string. If a property is managed
 296      * by this manager, its value shall not be null.
 297      */
 298     public String getLimitAsString(String propertyName) {
 299         int index = getIndex(propertyName);
 300         if (index > -1) {
 301             return getLimitValueByIndex(index);
 302         }
 303 
 304         return null;
 305     }
 306 
 307     /**
 308      * Return the value of a property by its ordinal
 309      *
 310      * @param limit the property
 311      * @return value of a property
 312      */
 313     public String getLimitValueAsString(Limit limit) {
 314         return Integer.toString(values[limit.ordinal()]);
 315     }
 316 
 317     /**
 318      * Return the value of the specified property
 319      *
 320      * @param limit the property
 321      * @return the value of the property
 322      */
 323     public int getLimit(Limit limit) {
 324         return values[limit.ordinal()];
 325     }
 326 
 327     /**
 328      * Return the value of a property by its ordinal
 329      *
 330      * @param index the index of a property
 331      * @return value of a property
 332      */
 333     public int getLimitByIndex(int index) {
 334         return values[index];
 335     }
 336     /**
 337      * Return the value of a property by its index
 338      *
 339      * @param index the index of a property
 340      * @return limit of a property as a string
 341      */
 342     public String getLimitValueByIndex(int index) {
 343         if (index == indexEntityCountInfo) {
 344             return printEntityCountInfo;
 345         }
 346 
 347         return Integer.toString(values[index]);
 348     }
 349     /**
 350      * Return the state of the limit property
 351      *
 352      * @param limit the limit
 353      * @return the state of the limit property
 354      */
 355     public State getState(Limit limit) {
 356         return states[limit.ordinal()];
 357     }
 358 
 359     /**
 360      * Return the state of the limit property
 361      *
 362      * @param limit the limit
 363      * @return the state of the limit property
 364      */
 365     public String getStateLiteral(Limit limit) {
 366         return states[limit.ordinal()].literal();
 367     }
 368 
 369     /**
 370      * Get the index by property name
 371      *
 372      * @param propertyName property name
 373      * @return the index of the property if found; return -1 if not
 374      */
 375     public int getIndex(String propertyName) {
 376         for (Limit limit : Limit.values()) {
 377             if (limit.equalsAPIPropertyName(propertyName)) {
 378                 //internally, ordinal is used as index
 379                 return limit.ordinal();
 380             }
 381         }
 382         //special property to return entity count info
 383         if (propertyName.equals(XalanConstants.JDK_ENTITY_COUNT_INFO)) {
 384             return indexEntityCountInfo;
 385         }
 386         return -1;
 387     }
 388 
 389     /**
 390      * Indicate if a property is set explicitly
 391      * @param index
 392      * @return
 393      */
 394     public boolean isSet(int index) {
 395         return isSet[index];
 396     }
 397 
 398     public boolean printEntityCountInfo() {
 399         return printEntityCountInfo.equals(XalanConstants.JDK_YES);
 400     }
 401     /**
 402      * Read from system properties, or those in jaxp.properties
 403      */
 404     private void readSystemProperties() {
 405 
 406         for (Limit limit : Limit.values()) {
 407             if (!getSystemProperty(limit, limit.systemProperty())) {
 408                 //if system property is not found, try the older form if any
 409                 for (NameMap nameMap : NameMap.values()) {
 410                     String oldName = nameMap.getOldName(limit.systemProperty());
 411                     if (oldName != null) {
 412                         getSystemProperty(limit, oldName);
 413                     }
 414                 }
 415             }
 416         }
 417 
 418     }
 419 
 420     // Array list to store printed warnings for each SAX parser used
 421     private static final CopyOnWriteArrayList<String> printedWarnings = new CopyOnWriteArrayList<>();
 422 
 423     /**
 424      * Prints out warnings if a parser does not support the specified feature/property.
 425      *
 426      * @param parserClassName the name of the parser class
 427      * @param propertyName the property name
 428      * @param exception the exception thrown by the parser
 429      */
 430     public static void printWarning(String parserClassName, String propertyName, SAXException exception) {
 431         String key = parserClassName+":"+propertyName;
 432         if (printedWarnings.addIfAbsent(key)) {
 433             System.err.println( "Warning: "+parserClassName+": "+exception.getMessage());
 434         }
 435     }
 436 
 437     /**
 438      * Read from system properties, or those in jaxp.properties
 439      *
 440      * @param property the type of the property
 441      * @param sysPropertyName the name of system property
 442      */
 443     private boolean getSystemProperty(Limit limit, String sysPropertyName) {
 444         try {
 445             String value = SecuritySupport.getSystemProperty(sysPropertyName);
 446             if (value != null && !value.equals("")) {
 447                 values[limit.ordinal()] = Integer.parseInt(value);
 448                 states[limit.ordinal()] = State.SYSTEMPROPERTY;
 449                 return true;
 450             }
 451 
 452             value = SecuritySupport.readJAXPProperty(sysPropertyName);
 453             if (value != null && !value.equals("")) {
 454                 values[limit.ordinal()] = Integer.parseInt(value);
 455                 states[limit.ordinal()] = State.JAXPDOTPROPERTIES;
 456                 return true;
 457             }
 458         } catch (NumberFormatException e) {
 459             //invalid setting
 460             throw new NumberFormatException("Invalid setting for system property: " + limit.systemProperty());
 461         }
 462         return false;
 463     }
 464 }
--- EOF ---