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.xalan.internal.utils; 27 28 import com.sun.org.apache.xalan.internal.XalanConstants; 29 import java.util.concurrent.CopyOnWriteArrayList; 30 import org.xml.sax.SAXException; 31 32 33 /** 34 * This class is not the same as that in Xerces. It is used to manage the 35 * state of corresponding Xerces properties and pass the values over to 36 * the Xerces Security Manager. 37 * 38 * @author Joe Wang Oracle Corp. 39 * 40 */ 41 public final class XMLSecurityManager { 42 43 /** 44 * States of the settings of a property, in the order: default value, value 45 * set by FEATURE_SECURE_PROCESSING, jaxp.properties file, jaxp system 46 * properties, and jaxp api properties 47 */ 48 public static enum State { 49 //this order reflects the overriding order 50 51 DEFAULT("default"), FSP("FEATURE_SECURE_PROCESSING"), 52 JAXPDOTPROPERTIES("jaxp.properties"), SYSTEMPROPERTY("system property"), 53 APIPROPERTY("property"); 54 55 final String literal; 56 State(String literal) { 57 this.literal = literal; 58 } 59 60 String literal() { 61 return literal; 62 } 63 } 64 65 /** 66 * Limits managed by the security manager 67 */ 68 public static enum Limit { 69 70 ENTITY_EXPANSION_LIMIT("EntityExpansionLimit", XalanConstants.JDK_ENTITY_EXPANSION_LIMIT, 71 XalanConstants.SP_ENTITY_EXPANSION_LIMIT, 0, 64000), 72 MAX_OCCUR_NODE_LIMIT("MaxOccurLimit", XalanConstants.JDK_MAX_OCCUR_LIMIT, 73 XalanConstants.SP_MAX_OCCUR_LIMIT, 0, 5000), 74 ELEMENT_ATTRIBUTE_LIMIT("ElementAttributeLimit", XalanConstants.JDK_ELEMENT_ATTRIBUTE_LIMIT, 75 XalanConstants.SP_ELEMENT_ATTRIBUTE_LIMIT, 0, 10000), 76 TOTAL_ENTITY_SIZE_LIMIT("TotalEntitySizeLimit", XalanConstants.JDK_TOTAL_ENTITY_SIZE_LIMIT, 77 XalanConstants.SP_TOTAL_ENTITY_SIZE_LIMIT, 0, 50000000), 78 GENERAL_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit", XalanConstants.JDK_GENERAL_ENTITY_SIZE_LIMIT, 79 XalanConstants.SP_GENERAL_ENTITY_SIZE_LIMIT, 0, 0), 80 PARAMETER_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit", XalanConstants.JDK_PARAMETER_ENTITY_SIZE_LIMIT, 81 XalanConstants.SP_PARAMETER_ENTITY_SIZE_LIMIT, 0, 1000000), 82 MAX_ELEMENT_DEPTH_LIMIT("MaxElementDepthLimit", XalanConstants.JDK_MAX_ELEMENT_DEPTH, 83 XalanConstants.SP_MAX_ELEMENT_DEPTH, 0, 0), 84 MAX_NAME_LIMIT("MaxXMLNameLimit", XalanConstants.JDK_XML_NAME_LIMIT, 85 XalanConstants.SP_XML_NAME_LIMIT, 1000, 1000); 86 87 final String key; 88 final String apiProperty; 89 final String systemProperty; 90 final int defaultValue; 91 final int secureValue; 92 93 Limit(String key, String apiProperty, String systemProperty, int value, int secureValue) { 94 this.key = key; 95 this.apiProperty = apiProperty; 96 this.systemProperty = systemProperty; 97 this.defaultValue = value; 98 this.secureValue = secureValue; 99 } 100 101 public boolean equalsAPIPropertyName(String propertyName) { 102 return (propertyName == null) ? false : apiProperty.equals(propertyName); 103 } 104 105 public boolean equalsSystemPropertyName(String propertyName) { 106 return (propertyName == null) ? false : systemProperty.equals(propertyName); 107 } 108 109 public String key() { 110 return key; 111 } 112 113 public String apiProperty() { 114 return apiProperty; 115 } 116 117 String systemProperty() { 118 return systemProperty; 119 } 120 121 public int defaultValue() { 122 return defaultValue; 123 } 124 125 int secureValue() { 126 return secureValue; 127 } 128 } 129 130 /** 131 * Map old property names with the new ones 132 */ 133 public static enum NameMap { 134 135 ENTITY_EXPANSION_LIMIT(XalanConstants.SP_ENTITY_EXPANSION_LIMIT, 136 XalanConstants.ENTITY_EXPANSION_LIMIT), 137 MAX_OCCUR_NODE_LIMIT(XalanConstants.SP_MAX_OCCUR_LIMIT, 138 XalanConstants.MAX_OCCUR_LIMIT), 139 ELEMENT_ATTRIBUTE_LIMIT(XalanConstants.SP_ELEMENT_ATTRIBUTE_LIMIT, 140 XalanConstants.ELEMENT_ATTRIBUTE_LIMIT); 141 final String newName; 142 final String oldName; 143 144 NameMap(String newName, String oldName) { 145 this.newName = newName; 146 this.oldName = oldName; 147 } 148 149 String getOldName(String newName) { 150 if (newName.equals(this.newName)) { 151 return oldName; 152 } 153 return null; 154 } 155 } 156 /** 157 * Values of the properties 158 */ 159 private final int[] values; 160 /** 161 * States of the settings for each property 162 */ 163 private State[] states; 164 /** 165 * States that determine if properties are set explicitly 166 */ 167 private boolean[] isSet; 168 169 170 /** 171 * Index of the special entityCountInfo property 172 */ 173 private final int indexEntityCountInfo = 10000; 174 private String printEntityCountInfo = ""; 175 176 /** 177 * Default constructor. Establishes default values for known security 178 * vulnerabilities. 179 */ 180 public XMLSecurityManager() { 181 this(false); 182 } 183 184 /** 185 * Instantiate Security Manager in accordance with the status of 186 * secure processing 187 * @param secureProcessing 188 */ 189 public XMLSecurityManager(boolean secureProcessing) { 190 values = new int[Limit.values().length]; 191 states = new State[Limit.values().length]; 192 isSet = new boolean[Limit.values().length]; 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 for (Limit limit : Limit.values()) { 211 if (secure) { 212 setLimit(limit.ordinal(), State.FSP, limit.secureValue()); 213 } else { 214 setLimit(limit.ordinal(), State.FSP, limit.defaultValue()); 215 } 216 } 217 } 218 219 /** 220 * Set limit by property name and state 221 * @param propertyName property name 222 * @param state the state of the property 223 * @param value the value of the property 224 * @return true if the property is managed by the security manager; false 225 * if otherwise. 226 */ 227 public boolean setLimit(String propertyName, State state, Object value) { 228 int index = getIndex(propertyName); 229 if (index > -1) { 230 setLimit(index, state, value); 231 return true; 232 } 233 return false; 234 } 235 236 /** 237 * Set the value for a specific limit. 238 * 239 * @param limit the limit 240 * @param state the state of the property 241 * @param value the value of the property 242 */ 243 public void setLimit(Limit limit, State state, int value) { 244 setLimit(limit.ordinal(), state, value); 245 } 246 247 /** 248 * Set the value of a property by its index 249 * 250 * @param index the index of the property 251 * @param state the state of the property 252 * @param value the value of the property 253 */ 254 public void setLimit(int index, State state, Object value) { 255 if (index == indexEntityCountInfo) { 256 //if it's explicitly set, it's treated as yes no matter the value 257 printEntityCountInfo = (String)value; 258 } else { 259 int temp = 0; 260 try { 261 temp = Integer.parseInt((String) value); 262 if (temp < 0) { 263 temp = 0; 264 } 265 } catch (NumberFormatException e) {} 266 setLimit(index, state, temp); } 267 } 268 269 /** 270 * Set the value of a property by its index 271 * 272 * @param index the index of the property 273 * @param state the state of the property 274 * @param value the value of the property 275 */ 276 public void setLimit(int index, State state, int value) { 277 if (index == indexEntityCountInfo) { 278 //if it's explicitly set, it's treated as yes no matter the value 279 printEntityCountInfo = XalanConstants.JDK_YES; 280 } else { 281 //only update if it shall override 282 if (state.compareTo(states[index]) >= 0) { 283 values[index] = value; 284 states[index] = state; 285 isSet[index] = true; 286 } 287 } 288 } 289 290 291 /** 292 * Return the value of the specified property. 293 * 294 * @param propertyName the property name 295 * @return the value of the property as a string. If a property is managed 296 * by this manager, its value shall not be null. 297 */ 298 public String getLimitAsString(String propertyName) { 299 int index = getIndex(propertyName); 300 if (index > -1) { 301 return getLimitValueByIndex(index); 302 } 303 304 return null; 305 } 306 307 /** 308 * Return the value of a property by its ordinal 309 * 310 * @param limit the property 311 * @return value of a property 312 */ 313 public String getLimitValueAsString(Limit limit) { 314 return Integer.toString(values[limit.ordinal()]); 315 } 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 index the index of a property 331 * @return value of a property 332 */ 333 public int getLimitByIndex(int index) { 334 return values[index]; 335 } 336 /** 337 * Return the value of a property by its index 338 * 339 * @param index the index of a property 340 * @return limit of a property as a string 341 */ 342 public String getLimitValueByIndex(int index) { 343 if (index == indexEntityCountInfo) { 344 return printEntityCountInfo; 345 } 346 347 return Integer.toString(values[index]); 348 } 349 /** 350 * Return the state of the limit property 351 * 352 * @param limit the limit 353 * @return the state of the limit property 354 */ 355 public State getState(Limit limit) { 356 return states[limit.ordinal()]; 357 } 358 359 /** 360 * Return the state of the limit property 361 * 362 * @param limit the limit 363 * @return the state of the limit property 364 */ 365 public String getStateLiteral(Limit limit) { 366 return states[limit.ordinal()].literal(); 367 } 368 369 /** 370 * Get the index by property name 371 * 372 * @param propertyName property name 373 * @return the index of the property if found; return -1 if not 374 */ 375 public int getIndex(String propertyName) { 376 for (Limit limit : Limit.values()) { 377 if (limit.equalsAPIPropertyName(propertyName)) { 378 //internally, ordinal is used as index 379 return limit.ordinal(); 380 } 381 } 382 //special property to return entity count info 383 if (propertyName.equals(XalanConstants.JDK_ENTITY_COUNT_INFO)) { 384 return indexEntityCountInfo; 385 } 386 return -1; 387 } 388 389 /** 390 * Indicate if a property is set explicitly 391 * @param index 392 * @return 393 */ 394 public boolean isSet(int index) { 395 return isSet[index]; 396 } 397 398 public boolean printEntityCountInfo() { 399 return printEntityCountInfo.equals(XalanConstants.JDK_YES); 400 } 401 /** 402 * Read from system properties, or those in jaxp.properties 403 */ 404 private void readSystemProperties() { 405 406 for (Limit limit : Limit.values()) { 407 if (!getSystemProperty(limit, limit.systemProperty())) { 408 //if system property is not found, try the older form if any 409 for (NameMap nameMap : NameMap.values()) { 410 String oldName = nameMap.getOldName(limit.systemProperty()); 411 if (oldName != null) { 412 getSystemProperty(limit, oldName); 413 } 414 } 415 } 416 } 417 418 } 419 420 // Array list to store printed warnings for each SAX parser used 421 private static final CopyOnWriteArrayList<String> printedWarnings = new CopyOnWriteArrayList<>(); 422 423 /** 424 * Prints out warnings if a parser does not support the specified feature/property. 425 * 426 * @param parserClassName the name of the parser class 427 * @param propertyName the property name 428 * @param exception the exception thrown by the parser 429 */ 430 public static void printWarning(String parserClassName, String propertyName, SAXException exception) { 431 String key = parserClassName+":"+propertyName; 432 if (printedWarnings.addIfAbsent(key)) { 433 System.err.println( "Warning: "+parserClassName+": "+exception.getMessage()); 434 } 435 } 436 437 /** 438 * Read from system properties, or those in jaxp.properties 439 * 440 * @param property the type of the property 441 * @param sysPropertyName the name of system property 442 */ 443 private boolean getSystemProperty(Limit limit, String sysPropertyName) { 444 try { 445 String value = SecuritySupport.getSystemProperty(sysPropertyName); 446 if (value != null && !value.equals("")) { 447 values[limit.ordinal()] = Integer.parseInt(value); 448 states[limit.ordinal()] = State.SYSTEMPROPERTY; 449 return true; 450 } 451 452 value = SecuritySupport.readJAXPProperty(sysPropertyName); 453 if (value != null && !value.equals("")) { 454 values[limit.ordinal()] = Integer.parseInt(value); 455 states[limit.ordinal()] = State.JAXPDOTPROPERTIES; 456 return true; 457 } 458 } catch (NumberFormatException e) { 459 //invalid setting 460 throw new NumberFormatException("Invalid setting for system property: " + limit.systemProperty()); 461 } 462 return false; 463 } 464 }