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.xerces.internal.utils;
  27 
  28 import com.sun.org.apache.xerces.internal.impl.Constants;
  29 import com.sun.org.apache.xerces.internal.util.SecurityManager;
  30 
  31 /**
  32  * This class manages standard and implementation-specific limitations.
  33  *
  34  */
  35 public final class XMLSecurityManager {
  36 
  37     /**
  38      * States of the settings of a property, in the order: default value, value
  39      * set by FEATURE_SECURE_PROCESSING, jaxp.properties file, jaxp system
  40      * properties, and jaxp api properties
  41      */
  42     public static enum State {
  43         //this order reflects the overriding order
  44 
  45         DEFAULT("default"), FSP("FEATURE_SECURE_PROCESSING"),
  46         JAXPDOTPROPERTIES("jaxp.properties"), SYSTEMPROPERTY("system property"),
  47         APIPROPERTY("property");
  48 
  49         final String literal;
  50         State(String literal) {
  51             this.literal = literal;
  52         }
  53 
  54         String literal() {
  55             return literal;
  56         }
  57     }
  58 
  59     /**
  60      * Limits managed by the security manager
  61      */
  62     public static enum Limit {
  63 
  64         ENTITY_EXPANSION_LIMIT(Constants.JDK_ENTITY_EXPANSION_LIMIT, Constants.SP_ENTITY_EXPANSION_LIMIT, 0, 64000),
  65         MAX_OCCUR_NODE_LIMIT(Constants.JDK_MAX_OCCUR_LIMIT, Constants.SP_MAX_OCCUR_LIMIT, 0, 5000),
  66         ELEMENT_ATTRIBUTE_LIMIT(Constants.JDK_ELEMENT_ATTRIBUTE_LIMIT, Constants.SP_ELEMENT_ATTRIBUTE_LIMIT, 0, 10000),
  67         TOTAL_ENTITY_SIZE_LIMIT(Constants.JDK_TOTAL_ENTITY_SIZE_LIMIT, Constants.SP_TOTAL_ENTITY_SIZE_LIMIT, 0, 50000000),
  68         GENERAL_ENTITY_SIZE_LIMIT(Constants.JDK_GENERAL_ENTITY_SIZE_LIMIT, Constants.SP_GENERAL_ENTITY_SIZE_LIMIT, 0, 0),
  69         PARAMETER_ENTITY_SIZE_LIMIT(Constants.JDK_PARAMETER_ENTITY_SIZE_LIMIT, Constants.SP_PARAMETER_ENTITY_SIZE_LIMIT, 0, 1000000),
  70         MAX_ELEMENT_DEPTH_LIMIT(Constants.JDK_MAX_ELEMENT_DEPTH, Constants.SP_MAX_ELEMENT_DEPTH, 0, 0);
  71 
  72         final String apiProperty;
  73         final String systemProperty;
  74         final int defaultValue;
  75         final int secureValue;
  76 
  77         Limit(String apiProperty, String systemProperty, int value, int secureValue) {
  78             this.apiProperty = apiProperty;
  79             this.systemProperty = systemProperty;
  80             this.defaultValue = value;
  81             this.secureValue = secureValue;
  82         }
  83 
  84         public boolean equalsAPIPropertyName(String propertyName) {
  85             return (propertyName == null) ? false : apiProperty.equals(propertyName);
  86         }
  87 
  88         public boolean equalsSystemPropertyName(String propertyName) {
  89             return (propertyName == null) ? false : systemProperty.equals(propertyName);
  90         }
  91 
  92         public String apiProperty() {
  93             return apiProperty;
  94         }
  95 
  96         String systemProperty() {
  97             return systemProperty;
  98         }
  99 
 100         int defaultValue() {
 101             return defaultValue;
 102         }
 103 
 104         int secureValue() {
 105             return secureValue;
 106         }
 107     }
 108 
 109     /**
 110      * Map old property names with the new ones
 111      */
 112     public static enum NameMap {
 113 
 114         ENTITY_EXPANSION_LIMIT(Constants.SP_ENTITY_EXPANSION_LIMIT, Constants.ENTITY_EXPANSION_LIMIT),
 115         MAX_OCCUR_NODE_LIMIT(Constants.SP_MAX_OCCUR_LIMIT, Constants.MAX_OCCUR_LIMIT),
 116         ELEMENT_ATTRIBUTE_LIMIT(Constants.SP_ELEMENT_ATTRIBUTE_LIMIT, Constants.ELEMENT_ATTRIBUTE_LIMIT);
 117         final String newName;
 118         final String oldName;
 119 
 120         NameMap(String newName, String oldName) {
 121             this.newName = newName;
 122             this.oldName = oldName;
 123         }
 124 
 125         String getOldName(String newName) {
 126             if (newName.equals(this.newName)) {
 127                 return oldName;
 128             }
 129             return null;
 130         }
 131     }
 132     private static final int NO_LIMIT = 0;
 133     /**
 134      * Values of the properties
 135      */
 136     private final int[] values;
 137     /**
 138      * States of the settings for each property
 139      */
 140     private State[] states;
 141     /**
 142      * Flag indicating if secure processing is set
 143      */
 144     boolean secureProcessing;
 145 
 146     /**
 147      * States that determine if properties are set explicitly
 148      */
 149     private boolean[] isSet;
 150 
 151 
 152     /**
 153      * Index of the special entityCountInfo property
 154      */
 155     private int indexEntityCountInfo = 10000;
 156     private String printEntityCountInfo = "";
 157 
 158     /**
 159      * Default constructor. Establishes default values for known security
 160      * vulnerabilities.
 161      */
 162     public XMLSecurityManager() {
 163         this(false);
 164     }
 165 
 166     /**
 167      * Instantiate Security Manager in accordance with the status of
 168      * secure processing
 169      * @param secureProcessing
 170      */
 171     public XMLSecurityManager(boolean secureProcessing) {
 172         values = new int[Limit.values().length];
 173         states = new State[Limit.values().length];
 174         isSet = new boolean[Limit.values().length];
 175         this.secureProcessing = secureProcessing;
 176         for (Limit limit : Limit.values()) {
 177             if (secureProcessing) {
 178                 values[limit.ordinal()] = limit.secureValue;
 179                 states[limit.ordinal()] = State.FSP;
 180             } else {
 181                 values[limit.ordinal()] = limit.defaultValue();
 182                 states[limit.ordinal()] = State.DEFAULT;
 183             }
 184         }
 185         //read system properties or jaxp.properties
 186         readSystemProperties();
 187     }
 188 
 189     /**
 190      * Setting FEATURE_SECURE_PROCESSING explicitly
 191      */
 192     public void setSecureProcessing(boolean secure) {
 193         secureProcessing = secure;
 194         for (Limit limit : Limit.values()) {
 195             if (secure) {
 196                 setLimit(limit.ordinal(), State.FSP, limit.secureValue());
 197             } else {
 198                 setLimit(limit.ordinal(), State.FSP, limit.defaultValue());
 199             }
 200         }
 201     }
 202 
 203     /**
 204      * Return the state of secure processing
 205      * @return the state of secure processing
 206      */
 207     public boolean isSecureProcessing() {
 208         return secureProcessing;
 209     }
 210 
 211 
 212     /**
 213      * Set limit by property name and state
 214      * @param propertyName property name
 215      * @param state the state of the property
 216      * @param value the value of the property
 217      * @return true if the property is managed by the security manager; false
 218      *              if otherwise.
 219      */
 220     public boolean setLimit(String propertyName, State state, Object value) {
 221         int index = getIndex(propertyName);
 222         if (index > -1) {
 223             setLimit(index, state, value);
 224             return true;
 225         }
 226         return false;
 227     }
 228 
 229     /**
 230      * Set the value for a specific limit.
 231      *
 232      * @param limit the limit
 233      * @param state the state of the property
 234      * @param value the value of the property
 235      */
 236     public void setLimit(Limit limit, State state, int value) {
 237         setLimit(limit.ordinal(), state, value);
 238     }
 239 
 240     /**
 241      * Set the value of a property by its index
 242      *
 243      * @param index the index of the property
 244      * @param state the state of the property
 245      * @param value the value of the property
 246      */
 247     public void setLimit(int index, State state, Object value) {
 248         if (index == indexEntityCountInfo) {
 249             printEntityCountInfo = (String)value;
 250         } else {
 251             int temp;
 252             if (Integer.class.isAssignableFrom(value.getClass())) {
 253                 temp = ((Integer)value).intValue();
 254             } else {
 255                 temp = Integer.parseInt((String) value);
 256                 if (temp < 0) {
 257                     temp = 0;
 258                 }
 259             }
 260             setLimit(index, state, temp);
 261         }
 262     }
 263 
 264     /**
 265      * Set the value of a property by its index
 266      *
 267      * @param index the index of the property
 268      * @param state the state of the property
 269      * @param value the value of the property
 270      */
 271     public void setLimit(int index, State state, int value) {
 272         if (index == indexEntityCountInfo) {
 273             //if it's explicitly set, it's treated as yes no matter the value
 274             printEntityCountInfo = Constants.JDK_YES;
 275         } else {
 276             //only update if it shall override
 277             if (state.compareTo(states[index]) >= 0) {
 278                 values[index] = value;
 279                 states[index] = state;
 280                 isSet[index] = true;
 281             }
 282         }
 283     }
 284 
 285     /**
 286      * Return the value of the specified property
 287      *
 288      * @param propertyName the property name
 289      * @return the value of the property as a string. If a property is managed
 290      * by this manager, its value shall not be null.
 291      */
 292     public String getLimitAsString(String propertyName) {
 293         int index = getIndex(propertyName);
 294         if (index > -1) {
 295             return getLimitValueByIndex(index);
 296         }
 297 
 298         return null;
 299     }
 300     /**
 301      * Return the value of the specified property
 302      *
 303      * @param limit the property
 304      * @return the value of the property
 305      */
 306     public int getLimit(Limit limit) {
 307         return values[limit.ordinal()];
 308     }
 309 
 310     /**
 311      * Return the value of a property by its ordinal
 312      *
 313      * @param limit the property
 314      * @return value of a property
 315      */
 316     public String getLimitValueAsString(Limit limit) {
 317         return Integer.toString(values[limit.ordinal()]);
 318     }
 319 
 320     /**
 321      * Return the value of a property by its ordinal
 322      *
 323      * @param index the index of a property
 324      * @return limit of a property as a string
 325      */
 326     public String getLimitValueByIndex(int index) {
 327         if (index == indexEntityCountInfo) {
 328             return printEntityCountInfo;
 329         }
 330 
 331         return Integer.toString(values[index]);
 332     }
 333 
 334     /**
 335      * Return the state of the limit property
 336      *
 337      * @param limit the limit
 338      * @return the state of the limit property
 339      */
 340     public State getState(Limit limit) {
 341         return states[limit.ordinal()];
 342     }
 343 
 344     /**
 345      * Return the state of the limit property
 346      *
 347      * @param limit the limit
 348      * @return the state of the limit property
 349      */
 350     public String getStateLiteral(Limit limit) {
 351         return states[limit.ordinal()].literal();
 352     }
 353 
 354     /**
 355      * Get the index by property name
 356      *
 357      * @param propertyName property name
 358      * @return the index of the property if found; return -1 if not
 359      */
 360     public int getIndex(String propertyName) {
 361         for (Limit limit : Limit.values()) {
 362             if (limit.equalsAPIPropertyName(propertyName)) {
 363                 //internally, ordinal is used as index
 364                 return limit.ordinal();
 365             }
 366         }
 367         //special property to return entity count info
 368         if (propertyName.equals(Constants.JDK_ENTITY_COUNT_INFO)) {
 369             return indexEntityCountInfo;
 370         }
 371         return -1;
 372     }
 373 
 374     /**
 375      * Check if there's no limit defined by the Security Manager
 376      * @param limit
 377      * @return
 378      */
 379     public boolean isNoLimit(int limit) {
 380         return limit==NO_LIMIT;
 381     }
 382     /**
 383      * Check if the size (length or count) of the specified limit property is
 384      * over the limit
 385      *
 386      * @param limit the type of the limit property
 387      * @param entityName the name of the entity
 388      * @param size the size (count or length) of the entity
 389      * @return true if the size is over the limit, false otherwise
 390      */
 391     public boolean isOverLimit(Limit limit, String entityName, int size,
 392             XMLLimitAnalyzer limitAnalyzer) {
 393         return isOverLimit(limit.ordinal(), entityName, size, limitAnalyzer);
 394     }
 395 
 396     /**
 397      * Check if the value (length or count) of the specified limit property is
 398      * over the limit
 399      *
 400      * @param index the index of the limit property
 401      * @param entityName the name of the entity
 402      * @param size the size (count or length) of the entity
 403      * @return true if the size is over the limit, false otherwise
 404      */
 405     public boolean isOverLimit(int index, String entityName, int size,
 406             XMLLimitAnalyzer limitAnalyzer) {
 407         if (values[index] == NO_LIMIT) {
 408             return false;
 409         }
 410         if (size > values[index]) {
 411             limitAnalyzer.addValue(index, entityName, size);
 412             return true;
 413         }
 414         return false;
 415     }
 416 
 417     /**
 418      * Check against cumulated value
 419      *
 420      * @param limit the type of the limit property
 421      * @param size the size (count or length) of the entity
 422      * @return true if the size is over the limit, false otherwise
 423      */
 424     public boolean isOverLimit(Limit limit, XMLLimitAnalyzer limitAnalyzer) {
 425         return isOverLimit(limit.ordinal(), limitAnalyzer);
 426     }
 427 
 428     public boolean isOverLimit(int index, XMLLimitAnalyzer limitAnalyzer) {
 429         if (values[index] == NO_LIMIT) {
 430             return false;
 431         }
 432 
 433         if (index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() ||
 434                 index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() ||
 435                 index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() ||
 436                 index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal()) {
 437             return (limitAnalyzer.getTotalValue(index) > values[index]);
 438         } else {
 439             return (limitAnalyzer.getValue(index) > values[index]);
 440         }
 441     }
 442 
 443     public void debugPrint(XMLLimitAnalyzer limitAnalyzer) {
 444         if (printEntityCountInfo.equals(Constants.JDK_YES)) {
 445             limitAnalyzer.debugPrint(this);
 446         }
 447     }
 448 
 449 
 450     /**
 451      * Indicate if a property is set explicitly
 452      * @param index
 453      * @return
 454      */
 455     public boolean isSet(int index) {
 456         return isSet[index];
 457     }
 458 
 459     public boolean printEntityCountInfo() {
 460         return printEntityCountInfo.equals(Constants.JDK_YES);
 461     }
 462 
 463     /**
 464      * Read from system properties, or those in jaxp.properties
 465      */
 466     private void readSystemProperties() {
 467 
 468         for (Limit limit : Limit.values()) {
 469             if (!getSystemProperty(limit, limit.systemProperty())) {
 470                 //if system property is not found, try the older form if any
 471                 for (NameMap nameMap : NameMap.values()) {
 472                     String oldName = nameMap.getOldName(limit.systemProperty());
 473                     if (oldName != null) {
 474                         getSystemProperty(limit, oldName);
 475                     }
 476                 }
 477             }
 478         }
 479 
 480     }
 481 
 482     /**
 483      * Read from system properties, or those in jaxp.properties
 484      *
 485      * @param property the type of the property
 486      * @param sysPropertyName the name of system property
 487      */
 488     private boolean getSystemProperty(Limit limit, String sysPropertyName) {
 489         try {
 490             String value = SecuritySupport.getSystemProperty(sysPropertyName);
 491             if (value != null && !value.equals("")) {
 492                 values[limit.ordinal()] = Integer.parseInt(value);
 493                 states[limit.ordinal()] = State.SYSTEMPROPERTY;
 494                 return true;
 495             }
 496 
 497             value = SecuritySupport.readJAXPProperty(sysPropertyName);
 498             if (value != null && !value.equals("")) {
 499                 values[limit.ordinal()] = Integer.parseInt(value);
 500                 states[limit.ordinal()] = State.JAXPDOTPROPERTIES;
 501                 return true;
 502             }
 503         } catch (NumberFormatException e) {
 504             //invalid setting
 505             throw new NumberFormatException("Invalid setting for system property: " + limit.systemProperty());
 506         }
 507         return false;
 508     }
 509 
 510 
 511     /**
 512      * Convert a value set through setProperty to XMLSecurityManager.
 513      * If the value is an instance of XMLSecurityManager, use it to override the default;
 514      * If the value is an old SecurityManager, convert to the new XMLSecurityManager.
 515      *
 516      * @param value user specified security manager
 517      * @param securityManager an instance of XMLSecurityManager
 518      * @return an instance of the new security manager XMLSecurityManager
 519      */
 520     static public XMLSecurityManager convert(Object value, XMLSecurityManager securityManager) {
 521         if (value == null) {
 522             if (securityManager == null) {
 523                 securityManager = new XMLSecurityManager(true);
 524             }
 525             return securityManager;
 526         }
 527         if (XMLSecurityManager.class.isAssignableFrom(value.getClass())) {
 528             return (XMLSecurityManager)value;
 529         } else {
 530             if (securityManager == null) {
 531                 securityManager = new XMLSecurityManager(true);
 532             }
 533             if (SecurityManager.class.isAssignableFrom(value.getClass())) {
 534                 SecurityManager origSM = (SecurityManager)value;
 535                 securityManager.setLimit(Limit.MAX_OCCUR_NODE_LIMIT, State.APIPROPERTY, origSM.getMaxOccurNodeLimit());
 536                 securityManager.setLimit(Limit.ENTITY_EXPANSION_LIMIT, State.APIPROPERTY, origSM.getEntityExpansionLimit());
 537                 securityManager.setLimit(Limit.ELEMENT_ATTRIBUTE_LIMIT, State.APIPROPERTY, origSM.getElementAttrLimit());
 538             }
 539             return securityManager;
 540         }
 541     }
 542 }