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