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