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 }