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