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 
  30 
  31 /**
  32  * This class is not the same as that in Xerces. It is used to manage the
  33  * state of corresponding Xerces properties and pass the values over to
  34  * the Xerces Security Manager.
  35  *
  36  * @author Joe Wang Oracle Corp.
  37  *
  38  */
  39 public final class XMLSecurityManager {
  40 
  41     /**
  42      * States of the settings of a property, in the order: default value, value
  43      * set by FEATURE_SECURE_PROCESSING, jaxp.properties file, jaxp system
  44      * properties, and jaxp api properties
  45      */
  46     public static enum State {
  47         //this order reflects the overriding order
  48 
  49         DEFAULT("default"), FSP("FEATURE_SECURE_PROCESSING"),
  50         JAXPDOTPROPERTIES("jaxp.properties"), SYSTEMPROPERTY("system property"),
  51         APIPROPERTY("property");
  52 
  53         final String literal;
  54         State(String literal) {
  55             this.literal = literal;
  56         }
  57 
  58         String literal() {
  59             return literal;
  60         }
  61     }
  62 
  63     /**
  64      * Limits managed by the security manager
  65      */
  66     public static enum Limit {
  67 
  68         ENTITY_EXPANSION_LIMIT("EntityExpansionLimit", XalanConstants.JDK_ENTITY_EXPANSION_LIMIT,
  69                 XalanConstants.SP_ENTITY_EXPANSION_LIMIT, 0, 64000),
  70         MAX_OCCUR_NODE_LIMIT("MaxOccurLimit", XalanConstants.JDK_MAX_OCCUR_LIMIT,
  71                 XalanConstants.SP_MAX_OCCUR_LIMIT, 0, 5000),
  72         ELEMENT_ATTRIBUTE_LIMIT("ElementAttributeLimit", XalanConstants.JDK_ELEMENT_ATTRIBUTE_LIMIT,
  73                 XalanConstants.SP_ELEMENT_ATTRIBUTE_LIMIT, 0, 10000),
  74         TOTAL_ENTITY_SIZE_LIMIT("TotalEntitySizeLimit", XalanConstants.JDK_TOTAL_ENTITY_SIZE_LIMIT,
  75                 XalanConstants.SP_TOTAL_ENTITY_SIZE_LIMIT, 0, 50000000),
  76         GENERAL_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit", XalanConstants.JDK_GENERAL_ENTITY_SIZE_LIMIT,
  77                 XalanConstants.SP_GENERAL_ENTITY_SIZE_LIMIT, 0, 0),
  78         PARAMETER_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit", XalanConstants.JDK_PARAMETER_ENTITY_SIZE_LIMIT,
  79                 XalanConstants.SP_PARAMETER_ENTITY_SIZE_LIMIT, 0, 1000000),
  80         MAX_ELEMENT_DEPTH_LIMIT("MaxElementDepthLimit", XalanConstants.JDK_MAX_ELEMENT_DEPTH,
  81                 XalanConstants.SP_MAX_ELEMENT_DEPTH, 0, 0),
  82         MAX_NAME_LIMIT("MaxXMLNameLimit", XalanConstants.JDK_XML_NAME_LIMIT,
  83                 XalanConstants.SP_XML_NAME_LIMIT, 1000, 1000);
  84 
  85         final String key;
  86         final String apiProperty;
  87         final String systemProperty;
  88         final int defaultValue;
  89         final int secureValue;
  90 
  91         Limit(String key, String apiProperty, String systemProperty, int value, int secureValue) {
  92             this.key = key;
  93             this.apiProperty = apiProperty;
  94             this.systemProperty = systemProperty;
  95             this.defaultValue = value;
  96             this.secureValue = secureValue;
  97         }
  98 
  99         public boolean equalsAPIPropertyName(String propertyName) {
 100             return (propertyName == null) ? false : apiProperty.equals(propertyName);
 101         }
 102 
 103         public boolean equalsSystemPropertyName(String propertyName) {
 104             return (propertyName == null) ? false : systemProperty.equals(propertyName);
 105         }
 106 
 107         public String key() {
 108             return key;
 109         }
 110 
 111         public String apiProperty() {
 112             return apiProperty;
 113         }
 114 
 115         String systemProperty() {
 116             return systemProperty;
 117         }
 118 
 119         public int defaultValue() {
 120             return defaultValue;
 121         }
 122 
 123         int secureValue() {
 124             return secureValue;
 125         }
 126     }
 127 
 128     /**
 129      * Map old property names with the new ones
 130      */
 131     public static enum NameMap {
 132 
 133         ENTITY_EXPANSION_LIMIT(XalanConstants.SP_ENTITY_EXPANSION_LIMIT,
 134                 XalanConstants.ENTITY_EXPANSION_LIMIT),
 135         MAX_OCCUR_NODE_LIMIT(XalanConstants.SP_MAX_OCCUR_LIMIT,
 136                 XalanConstants.MAX_OCCUR_LIMIT),
 137         ELEMENT_ATTRIBUTE_LIMIT(XalanConstants.SP_ELEMENT_ATTRIBUTE_LIMIT,
 138                 XalanConstants.ELEMENT_ATTRIBUTE_LIMIT);
 139         final String newName;
 140         final String oldName;
 141 
 142         NameMap(String newName, String oldName) {
 143             this.newName = newName;
 144             this.oldName = oldName;
 145         }
 146 
 147         String getOldName(String newName) {
 148             if (newName.equals(this.newName)) {
 149                 return oldName;
 150             }
 151             return null;
 152         }
 153     }
 154     /**
 155      * Values of the properties
 156      */
 157     private final int[] values;
 158     /**
 159      * States of the settings for each property
 160      */
 161     private State[] states;
 162     /**
 163      * States that determine if properties are set explicitly
 164      */
 165     private boolean[] isSet;
 166 
 167 
 168     /**
 169      * Index of the special entityCountInfo property
 170      */
 171     private final int indexEntityCountInfo = 10000;
 172     private String printEntityCountInfo = "";
 173 
 174     /**
 175      * Default constructor. Establishes default values for known security
 176      * vulnerabilities.
 177      */
 178     public XMLSecurityManager() {
 179         this(false);
 180     }
 181 
 182     /**
 183      * Instantiate Security Manager in accordance with the status of
 184      * secure processing
 185      * @param secureProcessing
 186      */
 187     public XMLSecurityManager(boolean secureProcessing) {
 188         values = new int[Limit.values().length];
 189         states = new State[Limit.values().length];
 190         isSet = new boolean[Limit.values().length];
 191         for (Limit limit : Limit.values()) {
 192             if (secureProcessing) {
 193                 values[limit.ordinal()] = limit.secureValue();
 194                 states[limit.ordinal()] = State.FSP;
 195             } else {
 196                 values[limit.ordinal()] = limit.defaultValue();
 197                 states[limit.ordinal()] = State.DEFAULT;
 198             }
 199         }
 200         //read system properties or jaxp.properties
 201         readSystemProperties();
 202     }
 203 
 204     /**
 205      * Setting FEATURE_SECURE_PROCESSING explicitly
 206      */
 207     public void setSecureProcessing(boolean secure) {
 208         for (Limit limit : Limit.values()) {
 209             if (secure) {
 210                 setLimit(limit.ordinal(), State.FSP, limit.secureValue());
 211             } else {
 212                 setLimit(limit.ordinal(), State.FSP, limit.defaultValue());
 213             }
 214         }
 215     }
 216 
 217     /**
 218      * Set limit by property name and state
 219      * @param propertyName property name
 220      * @param state the state of the property
 221      * @param value the value of the property
 222      * @return true if the property is managed by the security manager; false
 223      *              if otherwise.
 224      */
 225     public boolean setLimit(String propertyName, State state, Object value) {
 226         int index = getIndex(propertyName);
 227         if (index > -1) {
 228             setLimit(index, state, value);
 229             return true;
 230         }
 231         return false;
 232     }
 233 
 234     /**
 235      * Set the value for a specific limit.
 236      *
 237      * @param limit the limit
 238      * @param state the state of the property
 239      * @param value the value of the property
 240      */
 241     public void setLimit(Limit limit, State state, int value) {
 242         setLimit(limit.ordinal(), state, value);
 243     }
 244 
 245     /**
 246      * Set the value of a property by its index
 247      *
 248      * @param index the index of the property
 249      * @param state the state of the property
 250      * @param value the value of the property
 251      */
 252     public void setLimit(int index, State state, Object value) {
 253         if (index == indexEntityCountInfo) {
 254             //if it's explicitly set, it's treated as yes no matter the value
 255             printEntityCountInfo = (String)value;
 256         } else {
 257             int temp = 0;
 258             try {
 259                 temp = Integer.parseInt((String) value);
 260                 if (temp < 0) {
 261                     temp = 0;
 262                 }
 263             } catch (NumberFormatException e) {}
 264             setLimit(index, state, temp);        }
 265     }
 266 
 267     /**
 268      * Set the value of a property by its index
 269      *
 270      * @param index the index of the property
 271      * @param state the state of the property
 272      * @param value the value of the property
 273      */
 274     public void setLimit(int index, State state, int value) {
 275         if (index == indexEntityCountInfo) {
 276             //if it's explicitly set, it's treated as yes no matter the value
 277             printEntityCountInfo = XalanConstants.JDK_YES;
 278         } else {
 279             //only update if it shall override
 280             if (state.compareTo(states[index]) >= 0) {
 281                 values[index] = value;
 282                 states[index] = state;
 283                 isSet[index] = true;
 284             }
 285         }
 286     }
 287 
 288 
 289     /**
 290      * Return the value of the specified property.
 291      *
 292      * @param propertyName the property name
 293      * @return the value of the property as a string. If a property is managed
 294      * by this manager, its value shall not be null.
 295      */
 296     public String getLimitAsString(String propertyName) {
 297         int index = getIndex(propertyName);
 298         if (index > -1) {
 299             return getLimitValueByIndex(index);
 300         }
 301 
 302         return null;
 303     }
 304 
 305     /**
 306      * Return the value of a property by its ordinal
 307      *
 308      * @param limit the property
 309      * @return value of a property
 310      */
 311     public String getLimitValueAsString(Limit limit) {
 312         return Integer.toString(values[limit.ordinal()]);
 313     }
 314 
 315     /**
 316      * Return the value of the specified property
 317      *
 318      * @param limit the property
 319      * @return the value of the property
 320      */
 321     public int getLimit(Limit limit) {
 322         return values[limit.ordinal()];
 323     }
 324 
 325     /**
 326      * Return the value of a property by its ordinal
 327      *
 328      * @param index the index of a property
 329      * @return value of a property
 330      */
 331     public int getLimitByIndex(int index) {
 332         return values[index];
 333     }
 334     /**
 335      * Return the value of a property by its index
 336      *
 337      * @param index the index of a property
 338      * @return limit of a property as a string
 339      */
 340     public String getLimitValueByIndex(int index) {
 341         if (index == indexEntityCountInfo) {
 342             return printEntityCountInfo;
 343         }
 344 
 345         return Integer.toString(values[index]);
 346     }
 347     /**
 348      * Return the state of the limit property
 349      *
 350      * @param limit the limit
 351      * @return the state of the limit property
 352      */
 353     public State getState(Limit limit) {
 354         return states[limit.ordinal()];
 355     }
 356 
 357     /**
 358      * Return the state of the limit property
 359      *
 360      * @param limit the limit
 361      * @return the state of the limit property
 362      */
 363     public String getStateLiteral(Limit limit) {
 364         return states[limit.ordinal()].literal();
 365     }
 366 
 367     /**
 368      * Get the index by property name
 369      *
 370      * @param propertyName property name
 371      * @return the index of the property if found; return -1 if not
 372      */
 373     public int getIndex(String propertyName) {
 374         for (Limit limit : Limit.values()) {
 375             if (limit.equalsAPIPropertyName(propertyName)) {
 376                 //internally, ordinal is used as index
 377                 return limit.ordinal();
 378             }
 379         }
 380         //special property to return entity count info
 381         if (propertyName.equals(XalanConstants.JDK_ENTITY_COUNT_INFO)) {
 382             return indexEntityCountInfo;
 383         }
 384         return -1;
 385     }
 386 
 387     /**
 388      * Indicate if a property is set explicitly
 389      * @param index
 390      * @return
 391      */
 392     public boolean isSet(int index) {
 393         return isSet[index];
 394     }
 395 
 396     public boolean printEntityCountInfo() {
 397         return printEntityCountInfo.equals(XalanConstants.JDK_YES);
 398     }
 399     /**
 400      * Read from system properties, or those in jaxp.properties
 401      */
 402     private void readSystemProperties() {
 403 
 404         for (Limit limit : Limit.values()) {
 405             if (!getSystemProperty(limit, limit.systemProperty())) {
 406                 //if system property is not found, try the older form if any
 407                 for (NameMap nameMap : NameMap.values()) {
 408                     String oldName = nameMap.getOldName(limit.systemProperty());
 409                     if (oldName != null) {
 410                         getSystemProperty(limit, oldName);
 411                     }
 412                 }
 413             }
 414         }
 415 
 416     }
 417 
 418     /**
 419      * Read from system properties, or those in jaxp.properties
 420      *
 421      * @param property the type of the property
 422      * @param sysPropertyName the name of system property
 423      */
 424     private boolean getSystemProperty(Limit limit, String sysPropertyName) {
 425         try {
 426             String value = SecuritySupport.getSystemProperty(sysPropertyName);
 427             if (value != null && !value.equals("")) {
 428                 values[limit.ordinal()] = Integer.parseInt(value);
 429                 states[limit.ordinal()] = State.SYSTEMPROPERTY;
 430                 return true;
 431             }
 432 
 433             value = SecuritySupport.readJAXPProperty(sysPropertyName);
 434             if (value != null && !value.equals("")) {
 435                 values[limit.ordinal()] = Integer.parseInt(value);
 436                 states[limit.ordinal()] = State.JAXPDOTPROPERTIES;
 437                 return true;
 438             }
 439         } catch (NumberFormatException e) {
 440             //invalid setting
 441             throw new NumberFormatException("Invalid setting for system property: " + limit.systemProperty());
 442         }
 443         return false;
 444     }
 445 }