/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.apache.batik.util;
import java.net.URL;
import java.security.Policy;
This is a helper class which helps applications enforce secure
script execution.
It is used by the Squiggle browser as well as the rasterizer.
This class can install a SecurityManager
for an application
and resolves whether the application runs in a development
environment or from a jar file (in other words, it resolves code-base
issues for the application).
Author: Vincent Hardy Version: $Id: ApplicationSecurityEnforcer.java 1808888 2017-09-19 14:22:11Z ssteiner $
/**
* This is a helper class which helps applications enforce secure
* script execution.
* <br>
* It is used by the Squiggle browser as well as the rasterizer.
* <br>
* This class can install a <code>SecurityManager</code> for an application
* and resolves whether the application runs in a development
* environment or from a jar file (in other words, it resolves code-base
* issues for the application).
* <br>
*
* @author <a href="mailto:vincent.hardy@sun.com">Vincent Hardy</a>
* @version $Id: ApplicationSecurityEnforcer.java 1808888 2017-09-19 14:22:11Z ssteiner $
*/
public class ApplicationSecurityEnforcer {
Message for the SecurityException thrown when there is already
a SecurityManager installed at the time Squiggle tries
to install its own security settings.
/**
* Message for the SecurityException thrown when there is already
* a SecurityManager installed at the time Squiggle tries
* to install its own security settings.
*/
public static final String EXCEPTION_ALIEN_SECURITY_MANAGER
= "ApplicationSecurityEnforcer.message.security.exception.alien.security.manager";
Message for the NullPointerException thrown when no policy
file can be found.
/**
* Message for the NullPointerException thrown when no policy
* file can be found.
*/
public static final String EXCEPTION_NO_POLICY_FILE
= "ApplicationSecurityEnforcer.message.null.pointer.exception.no.policy.file";
System property for specifying an additional policy file.
/**
* System property for specifying an additional policy file.
*/
public static final String PROPERTY_JAVA_SECURITY_POLICY
= "java.security.policy";
Files in a jar file have a URL with the jar protocol
/**
* Files in a jar file have a URL with the jar protocol
*/
public static final String JAR_PROTOCOL
= "jar:";
Used in jar file urls to separate the jar file name
from the referenced file
/**
* Used in jar file urls to separate the jar file name
* from the referenced file
*/
public static final String JAR_URL_FILE_SEPARATOR
= "!/";
System property for App's development base directory
/**
* System property for App's development base directory
*/
public static final String PROPERTY_APP_DEV_BASE
= "app.dev.base";
System property for App's jars base directory
/**
* System property for App's jars base directory
*/
public static final String PROPERTY_APP_JAR_BASE
= "app.jar.base";
Directory where classes are expanded in the development
version
/**
* Directory where classes are expanded in the development
* version
*/
public static final String APP_MAIN_CLASS_DIR
= "classes/";
The application's main entry point
/**
* The application's main entry point
*/
protected Class appMainClass;
The application's security policy
/**
* The application's security policy
*/
protected String securityPolicy;
The resource name for the application's main class
/**
* The resource name for the application's main class
*/
protected String appMainClassRelativeURL;
Keeps track of the last SecurityManager installed
/**
* Keeps track of the last SecurityManager installed
*/
protected BatikSecurityManager lastSecurityManagerInstalled;
Creates a new ApplicationSecurityEnforcer.
Params: - appMainClass – class of the applications's main entry point
- securityPolicy – resource for the security policy which
should be enforced for the application.
- appJarFile – the Jar file into which the application is
packaged.
Deprecated: This constructor is now deprecated. Use the two
argument constructor instead as this version will
be removed after the 1.5beta4 release.
/**
* Creates a new ApplicationSecurityEnforcer.
* @param appMainClass class of the applications's main entry point
* @param securityPolicy resource for the security policy which
* should be enforced for the application.
* @param appJarFile the Jar file into which the application is
* packaged.
* @deprecated This constructor is now deprecated. Use the two
* argument constructor instead as this version will
* be removed after the 1.5beta4 release.
*/
public ApplicationSecurityEnforcer(Class appMainClass,
String securityPolicy,
String appJarFile){
this(appMainClass, securityPolicy);
}
Creates a new ApplicationSecurityEnforcer.
Params: - appMainClass – class of the applications's main entry point
- securityPolicy – resource for the security policy which
should be enforced for the application.
/**
* Creates a new ApplicationSecurityEnforcer.
* @param appMainClass class of the applications's main entry point
* @param securityPolicy resource for the security policy which
* should be enforced for the application.
*/
public ApplicationSecurityEnforcer(Class appMainClass,
String securityPolicy){
this.appMainClass = appMainClass;
this.securityPolicy = securityPolicy;
this.appMainClassRelativeURL =
appMainClass.getName().replace('.', '/')
+
".class";
}
Enforces security by installing a SecurityManager
.
This will throw a SecurityException
if installing
a SecurityManager
requires overriding an existing
SecurityManager
. In other words, this method will
not install a new SecurityManager
if there is
already one it did not install in place.
/**
* Enforces security by installing a <code>SecurityManager</code>.
* This will throw a <code>SecurityException</code> if installing
* a <code>SecurityManager</code> requires overriding an existing
* <code>SecurityManager</code>. In other words, this method will
* not install a new <code>SecurityManager</code> if there is
* already one it did not install in place.
*/
public void enforceSecurity(boolean enforce){
SecurityManager sm = System.getSecurityManager();
if (sm != null && sm != lastSecurityManagerInstalled) {
// Throw a Security exception: we do not want to override
// an 'alien' SecurityManager with either null or
// a new SecurityManager.
throw new SecurityException
(Messages.getString(EXCEPTION_ALIEN_SECURITY_MANAGER));
}
if (enforce) {
// We first set the security manager to null to
// force reloading of the policy file in case there
// has been a change since it was last enforced (this
// may happen with dynamically generated policy files).
System.setSecurityManager(null);
installSecurityManager();
} else {
if (sm != null) {
System.setSecurityManager(null);
lastSecurityManagerInstalled = null;
}
}
}
Returns the url for the default policy. This never
returns null, but it may throw a NullPointerException
/**
* Returns the url for the default policy. This never
* returns null, but it may throw a NullPointerException
*/
public URL getPolicyURL() {
ClassLoader cl = appMainClass.getClassLoader();
URL policyURL = cl.getResource(securityPolicy);
if (policyURL == null) {
throw new NullPointerException
(Messages.formatMessage(EXCEPTION_NO_POLICY_FILE,
new Object[]{securityPolicy}));
}
return policyURL;
}
Installs a SecurityManager on behalf of the application
/**
* Installs a SecurityManager on behalf of the application
*/
public void installSecurityManager(){
Policy policy = Policy.getPolicy();
BatikSecurityManager securityManager = new BatikSecurityManager();
//
// If there is a java.security.policy property defined,
// it takes precedence over the one passed to this object.
// Otherwise, we default to the one passed to the constructor
//
ClassLoader cl = appMainClass.getClassLoader();
String securityPolicyProperty
= System.getProperty(PROPERTY_JAVA_SECURITY_POLICY);
if (securityPolicyProperty == null || securityPolicyProperty.equals("")) {
// Specify app's security policy in the
// system property.
URL policyURL = getPolicyURL();
System.setProperty(PROPERTY_JAVA_SECURITY_POLICY,
policyURL.toString());
}
//
// The following detects whether the application is running in the
// development environment, in which case it will set the
// app.dev.base property or if it is running in the binary
// distribution, in which case it will set the app.jar.base
// property. These properties are expanded in the security
// policy files.
// Property expansion is used to provide portability of the
// policy files between various code bases (e.g., file base,
// server base, etc..).
//
URL mainClassURL = cl.getResource(appMainClassRelativeURL);
if (mainClassURL == null){
// Something is really wrong: we would be running a class
// which can't be found....
throw new RuntimeException(appMainClassRelativeURL);
}
String expandedMainClassName = mainClassURL.toString();
if (expandedMainClassName.startsWith(JAR_PROTOCOL) ) {
setJarBase(expandedMainClassName);
} else {
setDevBase(expandedMainClassName);
}
// Install new security manager
System.setSecurityManager(securityManager);
lastSecurityManagerInstalled = securityManager;
// Forces re-loading of the security policy
policy.refresh();
if (securityPolicyProperty == null || securityPolicyProperty.equals("")) {
System.setProperty(PROPERTY_JAVA_SECURITY_POLICY, "");
}
}
private void setJarBase(String expandedMainClassName){
//
// Only set the app.jar.base if it is not already defined
//
String curAppJarBase = System.getProperty(PROPERTY_APP_JAR_BASE);
if (curAppJarBase == null) {
expandedMainClassName = expandedMainClassName.substring(JAR_PROTOCOL.length());
int codeBaseEnd =
expandedMainClassName.indexOf(JAR_URL_FILE_SEPARATOR +
appMainClassRelativeURL);
if (codeBaseEnd == -1){
// Something is seriously wrong. This should *never* happen
// as the APP_SECURITY_POLICY_URL is such that it will be
// a substring of its corresponding URL value
throw new RuntimeException();
}
String appCodeBase = expandedMainClassName.substring(0, codeBaseEnd);
// At this point appCodeBase contains the JAR file name
// Now, we extract it.
codeBaseEnd = appCodeBase.lastIndexOf('/');
if (codeBaseEnd == -1) {
appCodeBase = "";
} else {
appCodeBase = appCodeBase.substring(0, codeBaseEnd);
}
System.setProperty(PROPERTY_APP_JAR_BASE, appCodeBase);
}
}
Position the app.dev.base property for expansion in
the policy file used when App is running in its
development version
/**
* Position the app.dev.base property for expansion in
* the policy file used when App is running in its
* development version
*/
private void setDevBase(String expandedMainClassName){
//
// Only set the app.code.base property if it is not already
// defined.
//
String curAppCodeBase = System.getProperty(PROPERTY_APP_DEV_BASE);
if (curAppCodeBase == null) {
int codeBaseEnd =
expandedMainClassName.indexOf(APP_MAIN_CLASS_DIR
+ appMainClassRelativeURL);
if (codeBaseEnd == -1){
// Something is seriously wrong. This should *never* happen
// as the APP_SECURITY_POLICY_URL is such that it will be
// a substring of its corresponding URL value
throw new RuntimeException();
}
String appCodeBase = expandedMainClassName.substring(0, codeBaseEnd);
System.setProperty(PROPERTY_APP_DEV_BASE, appCodeBase);
}
}
}