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 }