blob: ca25b08a5a779be55bee0e4ecc08ec841135f083 [file] [log] [blame]
/*
* 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.tools.ant.types;
import java.lang.reflect.Constructor;
import java.security.UnresolvedPermission;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.ExitException;
/**
* This class implements a security manager meant for usage by tasks that run inside the
* Ant VM. An examples are the Java Task and JUnitTask.
*
* The basic functionality is that nothing (except for a base set of permissions) is allowed, unless
* the permission is granted either explicitly or implicitly.
* If a permission is granted this can be overruled by explicitly revoking the permission.
*
* It is not permissible to add permissions (either granted or revoked) while the Security Manager
* is active (after calling setSecurityManager() but before calling restoreSecurityManager()).
*
* @since Ant 1.6
*/
public class Permissions {
private List grantedPermissions = new LinkedList();
private List revokedPermissions = new LinkedList();
private java.security.Permissions granted = null;
private SecurityManager origSm = null;
private boolean active = false;
private boolean delegateToOldSM;
// Mandatory constructor for permission object.
private static final Class[] PARAMS = {String.class, String.class};
/**
* Create a set of Permissions. Equivalent to calling
* <code>new Permissions(false)</code>.
*/
public Permissions() {
this(false);
}
/**
* Create a set of permissions.
* @param delegateToOldSM if <code>true</code> the old security manager
* will be used if the permission has not been explicitly granted or revoked
* in this instance.
*/
public Permissions(boolean delegateToOldSM) {
this.delegateToOldSM = delegateToOldSM;
}
/**
* Adds a permission to be granted.
* @param perm The Permissions.Permission to be granted.
*/
public void addConfiguredGrant(Permissions.Permission perm) {
grantedPermissions.add(perm);
}
/**
* Adds a permission to be revoked.
* @param perm The Permissions.Permission to be revoked
*/
public void addConfiguredRevoke(Permissions.Permission perm) {
revokedPermissions.add(perm);
}
/**
* To be used by tasks wishing to use this security model before executing the part to be
* subject to these Permissions. Note that setting the SecurityManager too early may
* prevent your part from starting, as for instance changing classloaders may be prohibited.
* The classloader for the new situation is supposed to be present.
* @throws BuildException on error
*/
public synchronized void setSecurityManager() throws BuildException {
origSm = System.getSecurityManager();
init();
System.setSecurityManager(new MySM());
active = true;
}
/**
* Initializes the list of granted permissions, checks the list of revoked permissions.
*/
private void init() throws BuildException {
granted = new java.security.Permissions();
for (Iterator i = revokedPermissions.listIterator(); i.hasNext();) {
Permissions.Permission p = (Permissions.Permission) i.next();
if (p.getClassName() == null) {
throw new BuildException("Revoked permission " + p + " does not contain a class.");
}
}
for (Iterator i = grantedPermissions.listIterator(); i.hasNext();) {
Permissions.Permission p = (Permissions.Permission) i.next();
if (p.getClassName() == null) {
throw new BuildException("Granted permission " + p
+ " does not contain a class.");
} else {
java.security.Permission perm = createPermission(p);
granted.add(perm);
}
}
// Add base set of permissions
granted.add(new java.net.SocketPermission("localhost:1024-", "listen"));
granted.add(new java.util.PropertyPermission("java.version", "read"));
granted.add(new java.util.PropertyPermission("java.vendor", "read"));
granted.add(new java.util.PropertyPermission("java.vendor.url", "read"));
granted.add(new java.util.PropertyPermission("java.class.version", "read"));
granted.add(new java.util.PropertyPermission("os.name", "read"));
granted.add(new java.util.PropertyPermission("os.version", "read"));
granted.add(new java.util.PropertyPermission("os.arch", "read"));
granted.add(new java.util.PropertyPermission("file.encoding", "read"));
granted.add(new java.util.PropertyPermission("file.separator", "read"));
granted.add(new java.util.PropertyPermission("path.separator", "read"));
granted.add(new java.util.PropertyPermission("line.separator", "read"));
granted.add(new java.util.PropertyPermission("java.specification.version", "read"));
granted.add(new java.util.PropertyPermission("java.specification.vendor", "read"));
granted.add(new java.util.PropertyPermission("java.specification.name", "read"));
granted.add(new java.util.PropertyPermission("java.vm.specification.version", "read"));
granted.add(new java.util.PropertyPermission("java.vm.specification.vendor", "read"));
granted.add(new java.util.PropertyPermission("java.vm.specification.name", "read"));
granted.add(new java.util.PropertyPermission("java.vm.version", "read"));
granted.add(new java.util.PropertyPermission("java.vm.vendor", "read"));
granted.add(new java.util.PropertyPermission("java.vm.name", "read"));
}
private java.security.Permission createPermission(
Permissions.Permission permission) {
try {
// First add explicitly already resolved permissions will not be
// resolved when added as unresolved permission.
Class clazz = Class.forName(permission.getClassName());
String name = permission.getName();
String actions = permission.getActions();
Constructor ctr = clazz.getConstructor(PARAMS);
return (java.security.Permission) ctr.newInstance(new Object[] {
name, actions });
} catch (Exception e) {
// Let the UnresolvedPermission handle it.
return new UnresolvedPermission(permission.getClassName(),
permission.getName(), permission.getActions(), null);
}
}
/**
* To be used by tasks that just finished executing the parts subject to these permissions.
*/
public synchronized void restoreSecurityManager() {
active = false;
System.setSecurityManager(origSm);
}
/**
* This inner class implements the actual SecurityManager that can be used by tasks
* supporting Permissions.
*/
private class MySM extends SecurityManager {
/**
* Exit is treated in a special way in order to be able to return the exit code
* towards tasks.
* An ExitException is thrown instead of a simple SecurityException to indicate the exit
* code.
* Overridden from java.lang.SecurityManager
* @param status The exit status requested.
*/
public void checkExit(int status) {
java.security.Permission perm = new java.lang.RuntimePermission("exitVM", null);
try {
checkPermission(perm);
} catch (SecurityException e) {
throw new ExitException(e.getMessage(), status);
}
}
/**
* The central point in checking permissions.
* Overridden from java.lang.SecurityManager
*
* @param perm The permission requested.
*/
public void checkPermission(java.security.Permission perm) {
if (active) {
if (delegateToOldSM && !perm.getName().equals("exitVM")) {
boolean permOK = false;
if (granted.implies(perm)) {
permOK = true;
}
checkRevoked(perm);
/*
if the permission was not explicitly granted or revoked
the original security manager will do its work
*/
if (!permOK && origSm != null) {
origSm.checkPermission(perm);
}
} else {
if (!granted.implies(perm)) {
throw new SecurityException("Permission " + perm + " was not granted.");
}
checkRevoked(perm);
}
}
}
/**
* throws an exception if this permission is revoked
* @param perm the permission being checked
*/
private void checkRevoked(java.security.Permission perm) {
for (Iterator i = revokedPermissions.listIterator(); i.hasNext();) {
if (((Permissions.Permission) i.next()).matches(perm)) {
throw new SecurityException("Permission " + perm + " was revoked.");
}
}
}
}
/** Represents a permission. */
public static class Permission {
private String className;
private String name;
private String actionString;
private Set actions;
/**
* Set the class, mandatory.
* @param aClass The class name of the permission.
*/
public void setClass(String aClass) {
className = aClass.trim();
}
/**
* Get the class of the permission.
* @return The class name of the permission.
*/
public String getClassName() {
return className;
}
/**
* Set the name of the permission.
* @param aName The name of the permission.
*/
public void setName(String aName) {
name = aName.trim();
}
/**
* Get the name of the permission.
* @return The name of the permission.
*/
public String getName() {
return name;
}
/**
* Set the actions.
* @param actions The actions of the permission.
*/
public void setActions(String actions) {
actionString = actions;
if (actions.length() > 0) {
this.actions = parseActions(actions);
}
}
/**
* Get the actions.
* @return The actions of the permission.
*/
public String getActions() {
return actionString;
}
/**
* Learn whether the permission matches in case of a revoked permission.
* @param perm The permission to check against.
*/
boolean matches(java.security.Permission perm) {
if (!className.equals(perm.getClass().getName())) {
return false;
}
if (name != null) {
if (name.endsWith("*")) {
if (!perm.getName().startsWith(name.substring(0, name.length() - 1))) {
return false;
}
} else {
if (!name.equals(perm.getName())) {
return false;
}
}
}
if (actions != null) {
Set as = parseActions(perm.getActions());
int size = as.size();
as.removeAll(actions);
if (as.size() == size) {
// None of the actions revoked, so all allowed.
return false;
}
}
return true;
}
/**
* Parses the actions into a set of separate strings.
* @param actions The actions to be parsed.
*/
private Set parseActions(String actions) {
Set result = new HashSet();
StringTokenizer tk = new StringTokenizer(actions, ",");
while (tk.hasMoreTokens()) {
String item = tk.nextToken().trim();
if (!item.equals("")) {
result.add(item);
}
}
return result;
}
/**
* Get a string description of the permissions.
* @return string description of the permissions.
*/
public String toString() {
return ("Permission: " + className + " (\"" + name + "\", \"" + actions + "\")");
}
}
}