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