001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.util;
020:
021: import java.net.URL;
022: import java.security.Policy;
023:
024: /**
025: * This is a helper class which helps applications enforce secure
026: * script execution.
027: * <br />
028: * It is used by the Squiggle browser as well as the rasterizer.
029: * <br />
030: * This class can install a <tt>SecurityManager</tt> for an application
031: * and resolves whether the application runs in a development
032: * environment or from a jar file (in other words, it resolves code-base
033: * issues for the application).
034: * <br />
035: *
036: * @author <a mailto="vincent.hardy@sun.com">Vincent Hardy</a>
037: * @version $Id: ApplicationSecurityEnforcer.java 475477 2006-11-15 22:44:28Z cam $
038: */
039: public class ApplicationSecurityEnforcer {
040: /**
041: * Message for the SecurityException thrown when there is already
042: * a SecurityManager installed at the time Squiggle tries
043: * to install its own security settings.
044: */
045: public static final String EXCEPTION_ALIEN_SECURITY_MANAGER = "ApplicationSecurityEnforcer.message.security.exception.alien.security.manager";
046:
047: /**
048: * Message for the NullPointerException thrown when no policy
049: * file can be found.
050: */
051: public static final String EXCEPTION_NO_POLICY_FILE = "ApplicationSecurityEnforcer.message.null.pointer.exception.no.policy.file";
052:
053: /**
054: * System property for specifying an additional policy file.
055: */
056: public static final String PROPERTY_JAVA_SECURITY_POLICY = "java.security.policy";
057:
058: /**
059: * Files in a jar file have a URL with the jar protocol
060: */
061: public static final String JAR_PROTOCOL = "jar:";
062:
063: /**
064: * Used in jar file urls to separate the jar file name
065: * from the referenced file
066: */
067: public static final String JAR_URL_FILE_SEPARATOR = "!/";
068:
069: /**
070: * System property for App's development base directory
071: */
072: public static final String PROPERTY_APP_DEV_BASE = "app.dev.base";
073:
074: /**
075: * System property for App's jars base directory
076: */
077: public static final String PROPERTY_APP_JAR_BASE = "app.jar.base";
078:
079: /**
080: * Directory where classes are expanded in the development
081: * version
082: */
083: public static final String APP_MAIN_CLASS_DIR = "classes/";
084:
085: /**
086: * The application's main entry point
087: */
088: protected Class appMainClass;
089:
090: /**
091: * The application's security policy
092: */
093: protected String securityPolicy;
094:
095: /**
096: * The resource name for the application's main class
097: */
098: protected String appMainClassRelativeURL;
099:
100: /**
101: * Keeps track of the last SecurityManager installed
102: */
103: protected BatikSecurityManager lastSecurityManagerInstalled;
104:
105: /**
106: * Creates a new ApplicationSecurityEnforcer.
107: * @param appMainClass class of the applications's main entry point
108: * @param securityPolicy resource for the security policy which
109: * should be enforced for the application.
110: * @param appJarFile the Jar file into which the application is
111: * packaged.
112: * @deprecated This constructor is now deprecated. Use the two
113: * argument constructor instead as this version will
114: * be removed after the 1.5beta4 release.
115: */
116: public ApplicationSecurityEnforcer(Class appMainClass,
117: String securityPolicy, String appJarFile) {
118: this (appMainClass, securityPolicy);
119: }
120:
121: /**
122: * Creates a new ApplicationSecurityEnforcer.
123: * @param appMainClass class of the applications's main entry point
124: * @param securityPolicy resource for the security policy which
125: * should be enforced for the application.
126: */
127: public ApplicationSecurityEnforcer(Class appMainClass,
128: String securityPolicy) {
129: this .appMainClass = appMainClass;
130: this .securityPolicy = securityPolicy;
131: this .appMainClassRelativeURL = appMainClass.getName().replace(
132: '.', '/')
133: + ".class";
134:
135: }
136:
137: /**
138: * Enforces security by installing a <tt>SecurityManager</tt>.
139: * This will throw a <tt>SecurityException</tt> if installing
140: * a <tt>SecurityManager</tt> requires overriding an existing
141: * <tt>SecurityManager</tt>. In other words, this method will
142: * not install a new <tt>SecurityManager</tt> if there is
143: * already one it did not install in place.
144: */
145: public void enforceSecurity(boolean enforce) {
146: SecurityManager sm = System.getSecurityManager();
147:
148: if (sm != null && sm != lastSecurityManagerInstalled) {
149: // Throw a Security exception: we do not want to override
150: // an 'alien' SecurityManager with either null or
151: // a new SecurityManager.
152: throw new SecurityException(Messages
153: .getString(EXCEPTION_ALIEN_SECURITY_MANAGER));
154: }
155:
156: if (enforce) {
157: // We first set the security manager to null to
158: // force reloading of the policy file in case there
159: // has been a change since it was last enforced (this
160: // may happen with dynamically generated policy files).
161: System.setSecurityManager(null);
162: installSecurityManager();
163: } else {
164: if (sm != null) {
165: System.setSecurityManager(null);
166: lastSecurityManagerInstalled = null;
167: }
168: }
169: }
170:
171: /**
172: * Returns the url for the default policy. This never
173: * returns null, but it may throw a NullPointerException
174: */
175: public URL getPolicyURL() {
176: ClassLoader cl = appMainClass.getClassLoader();
177: URL policyURL = cl.getResource(securityPolicy);
178:
179: if (policyURL == null) {
180: throw new NullPointerException(Messages.formatMessage(
181: EXCEPTION_NO_POLICY_FILE,
182: new Object[] { securityPolicy }));
183: }
184:
185: return policyURL;
186: }
187:
188: /**
189: * Installs a SecurityManager on behalf of the application
190: */
191: public void installSecurityManager() {
192: Policy policy = Policy.getPolicy();
193: BatikSecurityManager securityManager = new BatikSecurityManager();
194:
195: //
196: // If there is a java.security.policy property defined,
197: // it takes precedence over the one passed to this object.
198: // Otherwise, we default to the one passed to the constructor
199: //
200: ClassLoader cl = appMainClass.getClassLoader();
201: String securityPolicyProperty = System
202: .getProperty(PROPERTY_JAVA_SECURITY_POLICY);
203:
204: if (securityPolicyProperty == null
205: || securityPolicyProperty.equals("")) {
206: // Specify app's security policy in the
207: // system property.
208: URL policyURL = getPolicyURL();
209:
210: System.setProperty(PROPERTY_JAVA_SECURITY_POLICY, policyURL
211: .toString());
212: }
213:
214: //
215: // The following detects whether the application is running in the
216: // development environment, in which case it will set the
217: // app.dev.base property or if it is running in the binary
218: // distribution, in which case it will set the app.jar.base
219: // property. These properties are expanded in the security
220: // policy files.
221: // Property expansion is used to provide portability of the
222: // policy files between various code bases (e.g., file base,
223: // server base, etc..).
224: //
225: URL mainClassURL = cl.getResource(appMainClassRelativeURL);
226: if (mainClassURL == null) {
227: // Something is really wrong: we would be running a class
228: // which can't be found....
229: throw new Error(appMainClassRelativeURL);
230: }
231:
232: String expandedMainClassName = mainClassURL.toString();
233: if (expandedMainClassName.startsWith(JAR_PROTOCOL)) {
234: setJarBase(expandedMainClassName);
235: } else {
236: setDevBase(expandedMainClassName);
237: }
238:
239: // Install new security manager
240: System.setSecurityManager(securityManager);
241: lastSecurityManagerInstalled = securityManager;
242:
243: // Forces re-loading of the security policy
244: policy.refresh();
245:
246: if (securityPolicyProperty == null
247: || securityPolicyProperty.equals("")) {
248: System.setProperty(PROPERTY_JAVA_SECURITY_POLICY, "");
249: }
250: }
251:
252: private void setJarBase(String expandedMainClassName) {
253: //
254: // Only set the app.jar.base if it is not already defined
255: //
256: String curAppJarBase = System
257: .getProperty(PROPERTY_APP_JAR_BASE);
258: if (curAppJarBase == null) {
259: expandedMainClassName = expandedMainClassName
260: .substring(JAR_PROTOCOL.length());
261:
262: int codeBaseEnd = expandedMainClassName
263: .indexOf(JAR_URL_FILE_SEPARATOR
264: + appMainClassRelativeURL);
265:
266: if (codeBaseEnd == -1) {
267: // Something is seriously wrong. This should *never* happen
268: // as the APP_SECURITY_POLICY_URL is such that it will be
269: // a substring of its corresponding URL value
270: throw new Error();
271: }
272:
273: String appCodeBase = expandedMainClassName.substring(0,
274: codeBaseEnd);
275:
276: // At this point appCodeBase contains the JAR file name
277: // Now, we extract it.
278: codeBaseEnd = appCodeBase.lastIndexOf('/');
279: if (codeBaseEnd == -1) {
280: appCodeBase = "";
281: } else {
282: appCodeBase = appCodeBase.substring(0, codeBaseEnd);
283: }
284:
285: System.setProperty(PROPERTY_APP_JAR_BASE, appCodeBase);
286: }
287: }
288:
289: /**
290: * Position the app.dev.base property for expansion in
291: * the policy file used when App is running in its
292: * development version
293: */
294: private void setDevBase(String expandedMainClassName) {
295: //
296: // Only set the app.code.base property if it is not already
297: // defined.
298: //
299: String curAppCodeBase = System
300: .getProperty(PROPERTY_APP_DEV_BASE);
301: if (curAppCodeBase == null) {
302: int codeBaseEnd = expandedMainClassName
303: .indexOf(APP_MAIN_CLASS_DIR
304: + appMainClassRelativeURL);
305:
306: if (codeBaseEnd == -1) {
307: // Something is seriously wrong. This should *never* happen
308: // as the APP_SECURITY_POLICY_URL is such that it will be
309: // a substring of its corresponding URL value
310: throw new Error();
311: }
312:
313: String appCodeBase = expandedMainClassName.substring(0,
314: codeBaseEnd);
315: System.setProperty(PROPERTY_APP_DEV_BASE, appCodeBase);
316: }
317: }
318:
319: }
|