001: /*
002:
003: ============================================================================
004: The Apache Software License, Version 1.1
005: ============================================================================
006:
007: Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
008:
009: Redistribution and use in source and binary forms, with or without modifica-
010: tion, are permitted provided that the following conditions are met:
011:
012: 1. Redistributions of source code must retain the above copyright notice,
013: this list of conditions and the following disclaimer.
014:
015: 2. Redistributions in binary form must reproduce the above copyright notice,
016: this list of conditions and the following disclaimer in the documentation
017: and/or other materials provided with the distribution.
018:
019: 3. The end-user documentation included with the redistribution, if any, must
020: include the following acknowledgment: "This product includes software
021: developed by the Apache Software Foundation (http://www.apache.org/)."
022: Alternately, this acknowledgment may appear in the software itself, if
023: and wherever such third-party acknowledgments normally appear.
024:
025: 4. The names "Batik" and "Apache Software Foundation" must not be
026: used to endorse or promote products derived from this software without
027: prior written permission. For written permission, please contact
028: apache@apache.org.
029:
030: 5. Products derived from this software may not be called "Apache", nor may
031: "Apache" appear in their name, without prior written permission of the
032: Apache Software Foundation.
033:
034: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
035: INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
036: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
037: APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
038: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
039: DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
040: OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
041: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
042: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
043: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
044:
045: This software consists of voluntary contributions made by many individuals
046: on behalf of the Apache Software Foundation. For more information on the
047: Apache Software Foundation, please see <http://www.apache.org/>.
048:
049: */
050:
051: package org.apache.batik.util;
052:
053: import java.net.URL;
054: import java.security.Policy;
055:
056: /**
057: * This is a helper class which helps applications enforce secure
058: * script execution.
059: * <br />
060: * It is used by the Squiggle browser as well as the rasterizer.
061: * <br />
062: * This class can install a <tt>SecurityManager</tt> for an application
063: * and resolves whether the application runs in a development
064: * environment or from a jar file (in other words, it resolves code-base
065: * issues for the application).
066: * <br />
067: *
068: * @author <a mailto="vincent.hardy@sun.com">Vincent Hardy</a>
069: * @version $Id$
070: */
071: public class ApplicationSecurityEnforcer {
072: /**
073: * Message for the SecurityException thrown when there is already
074: * a SecurityManager installed at the time Squiggle tries
075: * to install its own security settings.
076: */
077: public static final String EXCEPTION_ALIEN_SECURITY_MANAGER = "ApplicationSecurityEnforcer.message.security.exception.alien.security.manager";
078:
079: /**
080: * Message for the NullPointerException thrown when no policy
081: * file can be found.
082: */
083: public static final String EXCEPTION_NO_POLICY_FILE = "ApplicationSecurityEnforcer.message.null.pointer.exception.no.policy.file";
084:
085: /**
086: * System property for specifying an additional policy file.
087: */
088: public static final String PROPERTY_JAVA_SECURITY_POLICY = "java.security.policy";
089:
090: /**
091: * Files in a jar file have a URL with the jar protocol
092: */
093: public static final String JAR_PROTOCOL = "jar:";
094:
095: /**
096: * Used in jar file urls to separate the jar file name
097: * from the referenced file
098: */
099: public static final String JAR_URL_FILE_SEPARATOR = "!/";
100:
101: /**
102: * System property for App's development base directory
103: */
104: public static final String PROPERTY_APP_DEV_BASE = "app.dev.base";
105:
106: /**
107: * System property for App's jars base directory
108: */
109: public static final String PROPERTY_APP_JAR_BASE = "app.jar.base";
110:
111: /**
112: * Directory where classes are expanded in the development
113: * version
114: */
115: public static final String APP_MAIN_CLASS_DIR = "classes/";
116:
117: /**
118: * The application's main entry point
119: */
120: protected Class appMainClass;
121:
122: /**
123: * The application's security policy
124: */
125: protected String securityPolicy;
126:
127: /**
128: * The resource name for the application's main class
129: */
130: protected String appMainClassRelativeURL;
131:
132: /**
133: * Keeps track of the last SecurityManager installed
134: */
135: protected BatikSecurityManager lastSecurityManagerInstalled;
136:
137: /**
138: * @param appClass class of the applications's main entry point
139: * @param securityPolicy resource for the security policy which
140: * should be enforced for the application.
141: * @param appJarFile the Jar file into which the application is
142: * packaged.
143: * @deprecated This constructor is now deprecated. Use the two
144: * argument constructor instead as this version will
145: * be removed after the 1.5beta4 release.
146: */
147: public ApplicationSecurityEnforcer(Class appMainClass,
148: String securityPolicy, String appJarFile) {
149: this (appMainClass, securityPolicy);
150: }
151:
152: /**
153: * @param appClass class of the applications's main entry point
154: * @param securityPolicy resource for the security policy which
155: * should be enforced for the application.
156: */
157: public ApplicationSecurityEnforcer(Class appMainClass,
158: String securityPolicy) {
159: this .appMainClass = appMainClass;
160: this .securityPolicy = securityPolicy;
161: this .appMainClassRelativeURL = appMainClass.getName().replace(
162: '.', '/')
163: + ".class";
164:
165: }
166:
167: /**
168: * Enforces security by installing a <tt>SecurityManager</tt>.
169: * This will throw a <tt>SecurityException</tt> if installing
170: * a <tt>SecurityManager</tt> requires overriding an existing
171: * <tt>SecurityManager</tt>. In other words, this method will
172: * not install a new <tt>SecurityManager</tt> if there is
173: * already one it did not install in place.
174: */
175: public void enforceSecurity(boolean enforce) {
176: SecurityManager sm = System.getSecurityManager();
177:
178: if (sm != null && sm != lastSecurityManagerInstalled) {
179: // Throw a Security exception: we do not want to override
180: // an 'alien' SecurityManager with either null or
181: // a new SecurityManager.
182: throw new SecurityException(Messages
183: .getString(EXCEPTION_ALIEN_SECURITY_MANAGER));
184: }
185:
186: if (enforce) {
187: // We first set the security manager to null to
188: // force reloading of the policy file in case there
189: // has been a change since it was last enforced (this
190: // may happen with dynamically generated policy files).
191: System.setSecurityManager(null);
192: installSecurityManager();
193: } else {
194: if (sm != null) {
195: System.setSecurityManager(null);
196: lastSecurityManagerInstalled = null;
197: }
198: }
199: }
200:
201: /**
202: * Returns the url for the default policy. This never
203: * returns null, but it may throw a NullPointerException
204: */
205: public URL getPolicyURL() {
206: ClassLoader cl = appMainClass.getClassLoader();
207: URL policyURL = cl.getResource(securityPolicy);
208:
209: if (policyURL == null) {
210: throw new NullPointerException(Messages.formatMessage(
211: EXCEPTION_NO_POLICY_FILE,
212: new Object[] { securityPolicy }));
213: }
214:
215: return policyURL;
216: }
217:
218: /**
219: * Installs a SecurityManager on behalf of the application
220: */
221: public void installSecurityManager() {
222: Policy policy = Policy.getPolicy();
223: BatikSecurityManager securityManager = new BatikSecurityManager();
224:
225: //
226: // If there is a java.security.policy property defined,
227: // it takes precedence over the one passed to this object.
228: // Otherwise, we default to the one passed to the constructor
229: //
230: ClassLoader cl = appMainClass.getClassLoader();
231: String securityPolicyProperty = System
232: .getProperty(PROPERTY_JAVA_SECURITY_POLICY);
233:
234: if (securityPolicyProperty == null
235: || securityPolicyProperty.equals("")) {
236: // Specify app's security policy in the
237: // system property.
238: URL policyURL = getPolicyURL();
239:
240: System.setProperty(PROPERTY_JAVA_SECURITY_POLICY, policyURL
241: .toString());
242: }
243:
244: //
245: // The following detects whether the application is running in the
246: // development environment, in which case it will set the
247: // app.dev.base property or if it is running in the binary
248: // distribution, in which case it will set the app.jar.base
249: // property. These properties are expanded in the security
250: // policy files.
251: // Property expansion is used to provide portability of the
252: // policy files between various code bases (e.g., file base,
253: // server base, etc..).
254: //
255: URL mainClassURL = cl.getResource(appMainClassRelativeURL);
256: if (mainClassURL == null) {
257: // Something is really wrong: we would be running a class
258: // which can't be found....
259: throw new Error(appMainClassRelativeURL);
260: }
261:
262: String expandedMainClassName = mainClassURL.toString();
263: if (expandedMainClassName.startsWith(JAR_PROTOCOL)) {
264: setJarBase(expandedMainClassName);
265: } else {
266: setDevBase(expandedMainClassName);
267: }
268:
269: // Install new security manager
270: System.setSecurityManager(securityManager);
271: lastSecurityManagerInstalled = securityManager;
272:
273: // Forces re-loading of the security policy
274: policy.refresh();
275:
276: if (securityPolicyProperty == null
277: || securityPolicyProperty.equals("")) {
278: System.setProperty(PROPERTY_JAVA_SECURITY_POLICY, "");
279: }
280: }
281:
282: private void setJarBase(String expandedMainClassName) {
283: //
284: // Only set the app.jar.base if it is not already defined
285: //
286: String curAppJarBase = System
287: .getProperty(PROPERTY_APP_JAR_BASE);
288: if (curAppJarBase == null) {
289: expandedMainClassName = expandedMainClassName
290: .substring(JAR_PROTOCOL.length());
291:
292: int codeBaseEnd = expandedMainClassName
293: .indexOf(JAR_URL_FILE_SEPARATOR
294: + appMainClassRelativeURL);
295:
296: if (codeBaseEnd == -1) {
297: // Something is seriously wrong. This should *never* happen
298: // as the APP_SECURITY_POLICY_URL is such that it will be
299: // a substring of its corresponding URL value
300: throw new Error();
301: }
302:
303: String appCodeBase = expandedMainClassName.substring(0,
304: codeBaseEnd);
305:
306: // At this point appCodeBase contains the JAR file name
307: // Now, we extract it.
308: codeBaseEnd = appCodeBase.lastIndexOf('/');
309: if (codeBaseEnd == -1) {
310: appCodeBase = "";
311: } else {
312: appCodeBase = appCodeBase.substring(0, codeBaseEnd);
313: }
314:
315: System.setProperty(PROPERTY_APP_JAR_BASE, appCodeBase);
316: }
317: }
318:
319: /**
320: * Position the app.dev.base property for expansion in
321: * the policy file used when App is running in its
322: * development version
323: */
324: private void setDevBase(String expandedMainClassName) {
325: //
326: // Only set the app.code.base property if it is not already
327: // defined.
328: //
329: String curAppCodeBase = System
330: .getProperty(PROPERTY_APP_DEV_BASE);
331: if (curAppCodeBase == null) {
332: int codeBaseEnd = expandedMainClassName
333: .indexOf(APP_MAIN_CLASS_DIR
334: + appMainClassRelativeURL);
335:
336: if (codeBaseEnd == -1) {
337: // Something is seriously wrong. This should *never* happen
338: // as the APP_SECURITY_POLICY_URL is such that it will be
339: // a substring of its corresponding URL value
340: throw new Error();
341: }
342:
343: String appCodeBase = expandedMainClassName.substring(0,
344: codeBaseEnd);
345: System.setProperty(PROPERTY_APP_DEV_BASE, appCodeBase);
346: }
347: }
348:
349: }
|