1 /*
   2  * Copyright (c) 2013, 2015, 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("EntityExpansionLimit",
  65                 Constants.JDK_ENTITY_EXPANSION_LIMIT, Constants.SP_ENTITY_EXPANSION_LIMIT, 0, 64000),
  66         MAX_OCCUR_NODE_LIMIT("MaxOccurLimit",
  67                 Constants.JDK_MAX_OCCUR_LIMIT, Constants.SP_MAX_OCCUR_LIMIT, 0, 5000),
  68         ELEMENT_ATTRIBUTE_LIMIT("ElementAttributeLimit",
  69                 Constants.JDK_ELEMENT_ATTRIBUTE_LIMIT, Constants.SP_ELEMENT_ATTRIBUTE_LIMIT, 0, 10000),
  70         TOTAL_ENTITY_SIZE_LIMIT("TotalEntitySizeLimit",
  71                 Constants.JDK_TOTAL_ENTITY_SIZE_LIMIT, Constants.SP_TOTAL_ENTITY_SIZE_LIMIT, 0, 50000000),
  72         GENERAL_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit",
  73                 Constants.JDK_GENERAL_ENTITY_SIZE_LIMIT, Constants.SP_GENERAL_ENTITY_SIZE_LIMIT, 0, 0),
  74         PARAMETER_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit",
  75                 Constants.JDK_PARAMETER_ENTITY_SIZE_LIMIT, Constants.SP_PARAMETER_ENTITY_SIZE_LIMIT, 0, 1000000),
  76         MAX_ELEMENT_DEPTH_LIMIT("MaxElementDepthLimit",
  77                 Constants.JDK_MAX_ELEMENT_DEPTH, Constants.SP_MAX_ELEMENT_DEPTH, 0, 0),
  78         MAX_NAME_LIMIT("MaxXMLNameLimit",
  79                 Constants.JDK_XML_NAME_LIMIT, Constants.SP_XML_NAME_LIMIT, 1000, 1000);
  80 
  81         final String key;
  82         final String apiProperty;
  83         final String systemProperty;
  84         final int defaultValue;
  85         final int secureValue;
  86 
  87         Limit(String key, String apiProperty, String systemProperty, int value, int secureValue) {
  88             this.key = key;
  89             this.apiProperty = apiProperty;
  90             this.systemProperty = systemProperty;
  91             this.defaultValue = value;
  92             this.secureValue = secureValue;
  93         }
  94 
  95         public boolean equalsAPIPropertyName(String propertyName) {
  96             return (propertyName == null) ? false : apiProperty.equals(propertyName);
  97         }
  98 
  99         public boolean equalsSystemPropertyName(String propertyName) {
 100             return (propertyName == null) ? false : systemProperty.equals(propertyName);
 101         }
 102 
 103         public String key() {
 104             return key;
 105         }
 106 
 107         public String apiProperty() {
 108             return apiProperty;
 109         }
 110 
 111         String systemProperty() {
 112             return systemProperty;
 113         }
 114 
 115         public int defaultValue() {
 116             return defaultValue;
 117         }
 118 
 119         int secureValue() {
 120             return secureValue;
 121         }
 122     }
 123 
 124     /**
 125      * Map old property names with the new ones
 126      */
 127     public static enum NameMap {
 128 
 129         ENTITY_EXPANSION_LIMIT(Constants.SP_ENTITY_EXPANSION_LIMIT, Constants.ENTITY_EXPANSION_LIMIT),
 130         MAX_OCCUR_NODE_LIMIT(Constants.SP_MAX_OCCUR_LIMIT, Constants.MAX_OCCUR_LIMIT),
 131         ELEMENT_ATTRIBUTE_LIMIT(Constants.SP_ELEMENT_ATTRIBUTE_LIMIT, Constants.ELEMENT_ATTRIBUTE_LIMIT);
 132         final String newName;
 133         final String oldName;
 134 
 135         NameMap(String newName, String oldName) {
 136             this.newName = newName;
 137             this.oldName = oldName;
 138         }
 139 
 140         String getOldName(String newName) {
 141             if (newName.equals(this.newName)) {
 142                 return oldName;
 143             }
 144             return null;
 145         }
 146     }
 147     private static final int NO_LIMIT = 0;
 148     /**
 149      * Values of the properties
 150      */
 151     private final int[] values;
 152     /**
 153      * States of the settings for each property
 154      */
 155     private State[] states;
 156     /**
 157      * Flag indicating if secure processing is set
 158      */
 159     boolean secureProcessing;
 160 
 161     /**
 162      * States that determine if properties are set explicitly
 163      */
 164     private boolean[] isSet;
 165 
 166 
 167     /**
 168      * Index of the special entityCountInfo property
 169      */
 170     private final int indexEntityCountInfo = 10000;
 171     private String printEntityCountInfo = "";
 172 
 173     /**
 174      * Default constructor. Establishes default values for known security
 175      * vulnerabilities.
 176      */
 177     public XMLSecurityManager() {
 178         this(false);
 179     }
 180 
 181     /**
 182      * Instantiate Security Manager in accordance with the status of
 183      * secure processing
 184      * @param secureProcessing
 185      */
 186     public XMLSecurityManager(boolean secureProcessing) {
 187         values = new int[Limit.values().length];
 188         states = new State[Limit.values().length];
 189         isSet = new boolean[Limit.values().length];
 190         this.secureProcessing = secureProcessing;
 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         secureProcessing = secure;
 209         for (Limit limit : Limit.values()) {
 210             if (secure) {
 211                 setLimit(limit.ordinal(), State.FSP, limit.secureValue());
 212             } else {
 213                 setLimit(limit.ordinal(), State.FSP, limit.defaultValue());
 214             }
 215         }
 216     }
 217 
 218     /**
 219      * Return the state of secure processing
 220      * @return the state of secure processing
 221      */
 222     public boolean isSecureProcessing() {
 223         return secureProcessing;
 224     }
 225 
 226 
 227     /**
 228      * Set limit by property name and state
 229      * @param propertyName property name
 230      * @param state the state of the property
 231      * @param value the value of the property
 232      * @return true if the property is managed by the security manager; false
 233      *              if otherwise.
 234      */
 235     public boolean setLimit(String propertyName, State state, Object value) {
 236         int index = getIndex(propertyName);
 237         if (index > -1) {
 238             setLimit(index, state, value);
 239             return true;
 240         }
 241         return false;
 242     }
 243 
 244     /**
 245      * Set the value for a specific limit.
 246      *
 247      * @param limit the limit
 248      * @param state the state of the property
 249      * @param value the value of the property
 250      */
 251     public void setLimit(Limit limit, State state, int value) {
 252         setLimit(limit.ordinal(), state, value);
 253     }
 254 
 255     /**
 256      * Set the value of a property by its index
 257      *
 258      * @param index the index of the property
 259      * @param state the state of the property
 260      * @param value the value of the property
 261      */
 262     public void setLimit(int index, State state, Object value) {
 263         if (index == indexEntityCountInfo) {
 264             printEntityCountInfo = (String)value;
 265         } else {
 266             int temp;
 267             if (Integer.class.isAssignableFrom(value.getClass())) {
 268                 temp = ((Integer)value).intValue();
 269             } else {
 270                 temp = Integer.parseInt((String) value);
 271                 if (temp < 0) {
 272                     temp = 0;
 273                 }
 274             }
 275             setLimit(index, state, temp);
 276         }
 277     }
 278 
 279     /**
 280      * Set the value of a property by its index
 281      *
 282      * @param index the index of the property
 283      * @param state the state of the property
 284      * @param value the value of the property
 285      */
 286     public void setLimit(int index, State state, int value) {
 287         if (index == indexEntityCountInfo) {
 288             //if it's explicitly set, it's treated as yes no matter the value
 289             printEntityCountInfo = Constants.JDK_YES;
 290         } else {
 291             //only update if it shall override
 292             if (state.compareTo(states[index]) >= 0) {
 293                 values[index] = value;
 294                 states[index] = state;
 295                 isSet[index] = true;
 296             }
 297         }
 298     }
 299 
 300     /**
 301      * Return the value of the specified property
 302      *
 303      * @param propertyName the property name
 304      * @return the value of the property as a string. If a property is managed
 305      * by this manager, its value shall not be null.
 306      */
 307     public String getLimitAsString(String propertyName) {
 308         int index = getIndex(propertyName);
 309         if (index > -1) {
 310             return getLimitValueByIndex(index);
 311         }
 312 
 313         return null;
 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 limit the property
 329      * @return value of a property
 330      */
 331     public String getLimitValueAsString(Limit limit) {
 332         return Integer.toString(values[limit.ordinal()]);
 333     }
 334 
 335     /**
 336      * Return the value of a property by its ordinal
 337      *
 338      * @param index the index of a property
 339      * @return limit of a property as a string
 340      */
 341     public String getLimitValueByIndex(int index) {
 342         if (index == indexEntityCountInfo) {
 343             return printEntityCountInfo;
 344         }
 345 
 346         return Integer.toString(values[index]);
 347     }
 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(Constants.JDK_ENTITY_COUNT_INFO)) {
 384             return indexEntityCountInfo;
 385         }
 386         return -1;
 387     }
 388 
 389     /**
 390      * Check if there's no limit defined by the Security Manager
 391      * @param limit
 392      * @return
 393      */
 394     public boolean isNoLimit(int limit) {
 395         return limit==NO_LIMIT;
 396     }
 397     /**
 398      * Check if the size (length or count) of the specified limit property is
 399      * over the limit
 400      *
 401      * @param limit the type of the limit property
 402      * @param entityName the name of the entity
 403      * @param size the size (count or length) of the entity
 404      * @return true if the size is over the limit, false otherwise
 405      */
 406     public boolean isOverLimit(Limit limit, String entityName, int size,
 407             XMLLimitAnalyzer limitAnalyzer) {
 408         return isOverLimit(limit.ordinal(), entityName, size, limitAnalyzer);
 409     }
 410 
 411     /**
 412      * Check if the value (length or count) of the specified limit property is
 413      * over the limit
 414      *
 415      * @param index the index of the limit property
 416      * @param entityName the name of the entity
 417      * @param size the size (count or length) of the entity
 418      * @return true if the size is over the limit, false otherwise
 419      */
 420     public boolean isOverLimit(int index, String entityName, int size,
 421             XMLLimitAnalyzer limitAnalyzer) {
 422         if (values[index] == NO_LIMIT) {
 423             return false;
 424         }
 425         if (size > values[index]) {
 426             limitAnalyzer.addValue(index, entityName, size);
 427             return true;
 428         }
 429         return false;
 430     }
 431 
 432     /**
 433      * Check against cumulated value
 434      *
 435      * @param limit the type of the limit property
 436      * @param size the size (count or length) of the entity
 437      * @return true if the size is over the limit, false otherwise
 438      */
 439     public boolean isOverLimit(Limit limit, XMLLimitAnalyzer limitAnalyzer) {
 440         return isOverLimit(limit.ordinal(), limitAnalyzer);
 441     }
 442 
 443     public boolean isOverLimit(int index, XMLLimitAnalyzer limitAnalyzer) {
 444         if (values[index] == NO_LIMIT) {
 445             return false;
 446         }
 447 
 448         if (index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() ||
 449                 index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() ||
 450                 index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() ||
 451                 index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() ||
 452                 index == Limit.MAX_NAME_LIMIT.ordinal()
 453                 ) {
 454             return (limitAnalyzer.getTotalValue(index) > values[index]);
 455         } else {
 456             return (limitAnalyzer.getValue(index) > values[index]);
 457         }
 458     }
 459 
 460     public void debugPrint(XMLLimitAnalyzer limitAnalyzer) {
 461         if (printEntityCountInfo.equals(Constants.JDK_YES)) {
 462             limitAnalyzer.debugPrint(this);
 463         }
 464     }
 465 
 466 
 467     /**
 468      * Indicate if a property is set explicitly
 469      * @param index
 470      * @return
 471      */
 472     public boolean isSet(int index) {
 473         return isSet[index];
 474     }
 475 
 476     public boolean printEntityCountInfo() {
 477         return printEntityCountInfo.equals(Constants.JDK_YES);
 478     }
 479 
 480     /**
 481      * Read from system properties, or those in jaxp.properties
 482      */
 483     private void readSystemProperties() {
 484 
 485         for (Limit limit : Limit.values()) {
 486             if (!getSystemProperty(limit, limit.systemProperty())) {
 487                 //if system property is not found, try the older form if any
 488                 for (NameMap nameMap : NameMap.values()) {
 489                     String oldName = nameMap.getOldName(limit.systemProperty());
 490                     if (oldName != null) {
 491                         getSystemProperty(limit, oldName);
 492                     }
 493                 }
 494             }
 495         }
 496 
 497     }
 498 
 499     /**
 500      * Read from system properties, or those in jaxp.properties
 501      *
 502      * @param property the type of the property
 503      * @param sysPropertyName the name of system property
 504      */
 505     private boolean getSystemProperty(Limit limit, String sysPropertyName) {
 506         try {
 507             String value = SecuritySupport.getSystemProperty(sysPropertyName);
 508             if (value != null && !value.equals("")) {
 509                 values[limit.ordinal()] = Integer.parseInt(value);
 510                 states[limit.ordinal()] = State.SYSTEMPROPERTY;
 511                 return true;
 512             }
 513 
 514             value = SecuritySupport.readJAXPProperty(sysPropertyName);
 515             if (value != null && !value.equals("")) {
 516                 values[limit.ordinal()] = Integer.parseInt(value);
 517                 states[limit.ordinal()] = State.JAXPDOTPROPERTIES;
 518                 return true;
 519             }
 520         } catch (NumberFormatException e) {
 521             //invalid setting
 522             throw new NumberFormatException("Invalid setting for system property: " + limit.systemProperty());
 523         }
 524         return false;
 525     }
 526 
 527 
 528     /**
 529      * Convert a value set through setProperty to XMLSecurityManager.
 530      * If the value is an instance of XMLSecurityManager, use it to override the default;
 531      * If the value is an old SecurityManager, convert to the new XMLSecurityManager.
 532      *
 533      * @param value user specified security manager
 534      * @param securityManager an instance of XMLSecurityManager
 535      * @return an instance of the new security manager XMLSecurityManager
 536      */
 537     static public XMLSecurityManager convert(Object value, XMLSecurityManager securityManager) {
 538         if (value == null) {
 539             if (securityManager == null) {
 540                 securityManager = new XMLSecurityManager(true);
 541             }
 542             return securityManager;
 543         }
 544         if (XMLSecurityManager.class.isAssignableFrom(value.getClass())) {
 545             return (XMLSecurityManager)value;
 546         } else {
 547             if (securityManager == null) {
 548                 securityManager = new XMLSecurityManager(true);
 549             }
 550             if (SecurityManager.class.isAssignableFrom(value.getClass())) {
 551                 SecurityManager origSM = (SecurityManager)value;
 552                 securityManager.setLimit(Limit.MAX_OCCUR_NODE_LIMIT, State.APIPROPERTY, origSM.getMaxOccurNodeLimit());
 553                 securityManager.setLimit(Limit.ENTITY_EXPANSION_LIMIT, State.APIPROPERTY, origSM.getEntityExpansionLimit());
 554                 securityManager.setLimit(Limit.ELEMENT_ATTRIBUTE_LIMIT, State.APIPROPERTY, origSM.getElementAttrLimit());
 555             }
 556             return securityManager;
 557         }
 558     }
 559 }