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         GENEAL_ENTITY_SIZE_LIMIT(Constants.JDK_GENEAL_ENTITY_SIZE_LIMIT, Constants.SP_GENEAL_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 
  71         final String apiProperty;
  72         final String systemProperty;
  73         final int defaultValue;
  74         final int secureValue;
  75 
  76         Limit(String apiProperty, String systemProperty, int value, int secureValue) {
  77             this.apiProperty = apiProperty;
  78             this.systemProperty = systemProperty;
  79             this.defaultValue = value;
  80             this.secureValue = secureValue;
  81         }
  82 
  83         public boolean equalsAPIPropertyName(String propertyName) {
  84             return (propertyName == null) ? false : apiProperty.equals(propertyName);
  85         }
  86 
  87         public boolean equalsSystemPropertyName(String propertyName) {
  88             return (propertyName == null) ? false : systemProperty.equals(propertyName);
  89         }
  90 
  91         public String apiProperty() {
  92             return apiProperty;
  93         }
  94 
  95         String systemProperty() {
  96             return systemProperty;
  97         }
  98 
  99         int defaultValue() {
 100             return defaultValue;
 101         }
 102 
 103         int secureValue() {
 104             return secureValue;
 105         }
 106     }
 107 
 108     /**
 109      * Map old property names with the new ones
 110      */
 111     public static enum NameMap {
 112 
 113         ENTITY_EXPANSION_LIMIT(Constants.SP_ENTITY_EXPANSION_LIMIT, Constants.ENTITY_EXPANSION_LIMIT),
 114         MAX_OCCUR_NODE_LIMIT(Constants.SP_MAX_OCCUR_LIMIT, Constants.MAX_OCCUR_LIMIT),
 115         ELEMENT_ATTRIBUTE_LIMIT(Constants.SP_ELEMENT_ATTRIBUTE_LIMIT, Constants.ELEMENT_ATTRIBUTE_LIMIT);
 116         final String newName;
 117         final String oldName;
 118 
 119         NameMap(String newName, String oldName) {
 120             this.newName = newName;
 121             this.oldName = oldName;
 122         }
 123 
 124         String getOldName(String newName) {
 125             if (newName.equals(this.newName)) {
 126                 return oldName;
 127             }
 128             return null;
 129         }
 130     }
 131     private static final int NO_LIMIT = 0;
 132     /**
 133      * Values of the properties
 134      */
 135     private int[] values;
 136     /**
 137      * States of the settings for each property
 138      */
 139     private State[] states;
 140     /**
 141      * Flag indicating if secure processing is set
 142      */
 143     boolean secureProcessing;
 144 
 145     /**
 146      * States that determine if properties are set explicitly
 147      */
 148     private boolean[] isSet;
 149 
 150 
 151     private XMLLimitAnalyzer limitAnalyzer;
 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         init();
 173         this.secureProcessing = secureProcessing;
 174         for (Limit limit : Limit.values()) {
 175             if (secureProcessing) {
 176                 values[limit.ordinal()] = limit.secureValue;
 177                 states[limit.ordinal()] = State.FSP;
 178             } else {
 179                 values[limit.ordinal()] = limit.defaultValue();
 180                 states[limit.ordinal()] = State.DEFAULT;
 181             }
 182         }
 183         //read system properties or jaxp.properties
 184         readSystemProperties();
 185     }
 186 
 187     /**
 188      * Clone a security manager
 189      * @param securityManager a base security manager
 190      */
 191     public XMLSecurityManager(XMLSecurityManager securityManager) {
 192         init();
 193         if (securityManager != null) {
 194             this.secureProcessing = securityManager.isSecureProcessing();
 195             for (Limit limit : Limit.values()) {
 196                 values[limit.ordinal()] = securityManager.getLimit(limit);
 197                 states[limit.ordinal()] = securityManager.getState(limit);
 198             }
 199         }
 200     }
 201 
 202     /**
 203      * Initialize values
 204      */
 205     private void init() {
 206         limitAnalyzer = new XMLLimitAnalyzer(this);
 207         int numOfElements = Limit.values().length;
 208         values = new int[numOfElements];
 209         states = new State[numOfElements];
 210         isSet = new boolean[numOfElements];        
 211     }
 212 
 213     /**
 214      * Reset all limits to their default status
 215      */
 216     public void resetLimits() {
 217         limitAnalyzer.reset();
 218     }
 219 
 220     /**
 221      * Setting FEATURE_SECURE_PROCESSING explicitly
 222      */
 223     public void setSecureProcessing(boolean secure) {
 224         secureProcessing = secure;
 225         for (Limit limit : Limit.values()) {
 226             if (secure) {
 227                 setLimit(limit.ordinal(), State.FSP, limit.secureValue());
 228             } else {
 229                 setLimit(limit.ordinal(), State.FSP, limit.defaultValue());
 230             }
 231         }
 232     }
 233 
 234     /**
 235      * Return the state of secure processing
 236      * @return the state of secure processing
 237      */
 238     public boolean isSecureProcessing() {
 239         return secureProcessing;
 240     }
 241 
 242 
 243     /**
 244      * Set limit by property name and state
 245      * @param propertyName property name
 246      * @param state the state of the property
 247      * @param value the value of the property
 248      * @return true if the property is managed by the security manager; false
 249      *              if otherwise.
 250      */
 251     public boolean setLimit(String propertyName, State state, Object value) {
 252         int index = getIndex(propertyName);
 253         if (index > -1) {
 254             setLimit(index, state, value);
 255             return true;
 256         }
 257         return false;
 258     }
 259 
 260     /**
 261      * Set the value for a specific limit.
 262      *
 263      * @param limit the limit
 264      * @param state the state of the property
 265      * @param value the value of the property
 266      */
 267     public void setLimit(Limit limit, State state, int value) {
 268         setLimit(limit.ordinal(), state, value);
 269     }
 270 
 271     /**
 272      * Set the value of a property by its index
 273      *
 274      * @param index the index of the property
 275      * @param state the state of the property
 276      * @param value the value of the property
 277      */
 278     public void setLimit(int index, State state, Object value) {
 279         if (index == indexEntityCountInfo) {
 280             printEntityCountInfo = (String)value;
 281         } else {
 282             int temp = 0;
 283             try {
 284                 temp = Integer.parseInt((String) value);
 285                 if (temp < 0) {
 286                     temp = 0;
 287                 }
 288             } catch (NumberFormatException e) {}
 289             setLimit(index, state, temp);
 290         }
 291     }
 292 
 293     /**
 294      * Set the value of a property by its index
 295      *
 296      * @param index the index of the property
 297      * @param state the state of the property
 298      * @param value the value of the property
 299      */
 300     public void setLimit(int index, State state, int value) {
 301         if (index == indexEntityCountInfo) {
 302             //if it's explicitly set, it's treated as yes no matter the value
 303             printEntityCountInfo = Constants.JDK_YES;
 304         } else {
 305             //only update if it shall override
 306             if (state.compareTo(states[index]) >= 0) {
 307                 values[index] = value;
 308                 states[index] = state;
 309                 isSet[index] = true;
 310             }
 311         }
 312     }
 313 
 314     /**
 315      * Return the value of the specified property
 316      *
 317      * @param propertyName the property name
 318      * @return the value of the property as a string. If a property is managed
 319      * by this manager, its value shall not be null.
 320      */
 321     public String getLimitAsString(String propertyName) {
 322         int index = getIndex(propertyName);
 323         if (index > -1) {
 324             return getLimitValueByIndex(index);
 325         }
 326 
 327         return null;
 328     }
 329     /**
 330      * Return the value of the specified property
 331      *
 332      * @param limit the property
 333      * @return the value of the property
 334      */
 335     public int getLimit(Limit limit) {
 336         return values[limit.ordinal()];
 337     }
 338 
 339     /**
 340      * Return the value of a property by its ordinal
 341      *
 342      * @param limit the property
 343      * @return value of a property
 344      */
 345     public String getLimitValueAsString(Limit limit) {
 346         return Integer.toString(values[limit.ordinal()]);
 347     }
 348 
 349     /**
 350      * Return the value of a property by its ordinal
 351      *
 352      * @param index the index of a property
 353      * @return limit of a property as a string
 354      */
 355     public String getLimitValueByIndex(int index) {
 356         if (index == indexEntityCountInfo) {
 357             return printEntityCountInfo;
 358         }
 359 
 360         return Integer.toString(values[index]);
 361     }
 362 
 363     /**
 364      * Return the state of the limit property
 365      *
 366      * @param limit the limit
 367      * @return the state of the limit property
 368      */
 369     public State getState(Limit limit) {
 370         return states[limit.ordinal()];
 371     }
 372 
 373     /**
 374      * Return the state of the limit property
 375      *
 376      * @param limit the limit
 377      * @return the state of the limit property
 378      */
 379     public String getStateLiteral(Limit limit) {
 380         return states[limit.ordinal()].literal();
 381     }
 382 
 383     /**
 384      * Get the index by property name
 385      *
 386      * @param propertyName property name
 387      * @return the index of the property if found; return -1 if not
 388      */
 389     public int getIndex(String propertyName) {
 390         for (Limit limit : Limit.values()) {
 391             if (limit.equalsAPIPropertyName(propertyName)) {
 392                 //internally, ordinal is used as index
 393                 return limit.ordinal();
 394             }
 395         }
 396         //special property to return entity count info
 397         if (propertyName.equals(Constants.JDK_ENTITY_COUNT_INFO)) {
 398             return indexEntityCountInfo;
 399         }
 400         return -1;
 401     }
 402 
 403     /**
 404      * Check if there's no limit defined by the Security Manager
 405      * @param limit
 406      * @return
 407      */
 408     public boolean isNoLimit(int limit) {
 409         return limit==NO_LIMIT;
 410     }
 411     /**
 412      * Check if the size (length or count) of the specified limit property is
 413      * over the limit
 414      *
 415      * @param limit the type 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(Limit limit, String entityName, int size) {
 421         return isOverLimit(limit.ordinal(), entityName, size);
 422     }
 423 
 424     /**
 425      * Check if the value (length or count) of the specified limit property is
 426      * over the limit
 427      *
 428      * @param index the index of the limit property
 429      * @param entityName the name of the entity
 430      * @param size the size (count or length) of the entity
 431      * @return true if the size is over the limit, false otherwise
 432      */
 433     public boolean isOverLimit(int index, String entityName, int size) {
 434         if (values[index] == NO_LIMIT) {
 435             return false;
 436         }
 437         if (size > values[index]) {
 438             limitAnalyzer.addValue(index, entityName, size);
 439             return true;
 440         }
 441         return false;
 442     }
 443 
 444     /**
 445      * Check against cumulated value
 446      *
 447      * @param limit the type of the limit property
 448      * @param size the size (count or length) of the entity
 449      * @return true if the size is over the limit, false otherwise
 450      */
 451     public boolean isOverLimit(Limit limit) {
 452         return isOverLimit(limit.ordinal());
 453     }
 454 
 455     public boolean isOverLimit(int index) {
 456         if (values[index] == NO_LIMIT) {
 457             return false;
 458         }
 459 
 460         if (index==Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() ||
 461                 index==Limit.ENTITY_EXPANSION_LIMIT.ordinal() ||
 462                 index==Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()) {
 463             return (limitAnalyzer.getTotalValue(index) > values[index]);
 464         } else {
 465             return (limitAnalyzer.getValue(index) > values[index]);
 466         }
 467     }
 468 
 469     public void debugPrint() {
 470         if (printEntityCountInfo.equals(Constants.JDK_YES)) {
 471             limitAnalyzer.debugPrint();
 472         }
 473     }
 474 
 475     /**
 476      * Return the limit analyzer
 477      *
 478      * @return the limit analyzer
 479      */
 480     public XMLLimitAnalyzer getLimitAnalyzer() {
 481         return limitAnalyzer;
 482     }
 483 
 484     /**
 485      * Set limit analyzer
 486      *
 487      * @param analyzer a limit analyzer
 488      */
 489     public void setLimitAnalyzer(XMLLimitAnalyzer analyzer) {
 490         limitAnalyzer = analyzer;
 491     }
 492 
 493     /**
 494      * Indicate if a property is set explicitly
 495      * @param index
 496      * @return
 497      */
 498     public boolean isSet(int index) {
 499         return isSet[index];
 500     }
 501 
 502     public boolean printEntityCountInfo() {
 503         return printEntityCountInfo.equals(Constants.JDK_YES);
 504     }
 505 
 506     /**
 507      * Read from system properties, or those in jaxp.properties
 508      */
 509     private void readSystemProperties() {
 510 
 511         for (Limit limit : Limit.values()) {
 512             if (!getSystemProperty(limit, limit.systemProperty())) {
 513                 //if system property is not found, try the older form if any
 514                 for (NameMap nameMap : NameMap.values()) {
 515                     String oldName = nameMap.getOldName(limit.systemProperty());
 516                     if (oldName != null) {
 517                         getSystemProperty(limit, oldName);
 518                     }
 519                 }
 520             }
 521         }
 522 
 523     }
 524 
 525     /**
 526      * Read from system properties, or those in jaxp.properties
 527      *
 528      * @param property the type of the property
 529      * @param sysPropertyName the name of system property
 530      */
 531     private boolean getSystemProperty(Limit limit, String sysPropertyName) {
 532         try {
 533             String value = SecuritySupport.getSystemProperty(sysPropertyName);
 534             if (value != null && !value.equals("")) {
 535                 values[limit.ordinal()] = Integer.parseInt(value);
 536                 states[limit.ordinal()] = State.SYSTEMPROPERTY;
 537                 return true;
 538             }
 539 
 540             value = SecuritySupport.readJAXPProperty(sysPropertyName);
 541             if (value != null && !value.equals("")) {
 542                 values[limit.ordinal()] = Integer.parseInt(value);
 543                 states[limit.ordinal()] = State.JAXPDOTPROPERTIES;
 544                 return true;
 545             }
 546         } catch (NumberFormatException e) {
 547             //invalid setting
 548             throw new NumberFormatException("Invalid setting for system property: " + limit.systemProperty());
 549         }
 550         return false;
 551     }
 552 
 553 
 554     /**
 555      * Convert a value set through setProperty to XMLSecurityManager.
 556      * If the value is an instance of XMLSecurityManager, use it to override the default;
 557      * If the value is an old SecurityManager, convert to the new XMLSecurityManager.
 558      *
 559      * @param value user specified security manager
 560      * @param securityManager an instance of XMLSecurityManager
 561      * @return an instance of the new security manager XMLSecurityManager
 562      */
 563     static public XMLSecurityManager convert(Object value, XMLSecurityManager securityManager) {
 564         if (value == null) {
 565             if (securityManager == null) {
 566                 securityManager = new XMLSecurityManager(true);
 567             }
 568             return securityManager;
 569         }
 570         if (XMLSecurityManager.class.isAssignableFrom(value.getClass())) {
 571             return (XMLSecurityManager)value;
 572         } else {
 573             if (securityManager == null) {
 574                 securityManager = new XMLSecurityManager(true);
 575             }
 576             if (SecurityManager.class.isAssignableFrom(value.getClass())) {
 577                 SecurityManager origSM = (SecurityManager)value;
 578                 securityManager.setLimit(Limit.MAX_OCCUR_NODE_LIMIT, State.APIPROPERTY, origSM.getMaxOccurNodeLimit());
 579                 securityManager.setLimit(Limit.ENTITY_EXPANSION_LIMIT, State.APIPROPERTY, origSM.getEntityExpansionLimit());
 580                 securityManager.setLimit(Limit.ELEMENT_ATTRIBUTE_LIMIT, State.APIPROPERTY, origSM.getElementAttrLimit());
 581             }
 582             return securityManager;
 583         }
 584     }
 585 }