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