|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package java.util; |
|
|
|
import java.io.IOException; |
|
import java.io.ObjectInputStream; |
|
import java.io.ObjectOutputStream; |
|
import java.io.ObjectStreamField; |
|
import java.io.Serializable; |
|
import java.security.*; |
|
import java.util.Enumeration; |
|
import java.util.Hashtable; |
|
import java.util.concurrent.ConcurrentHashMap; |
|
import sun.security.util.SecurityConstants; |
|
|
|
/** |
|
* This class is for property permissions. |
|
* |
|
* <P> |
|
* The name is the name of the property ("java.home", |
|
* "os.name", etc). The naming |
|
* convention follows the hierarchical property naming convention. |
|
* Also, an asterisk |
|
* may appear at the end of the name, following a ".", or by itself, to |
|
* signify a wildcard match. For example: "java.*" and "*" signify a wildcard |
|
* match, while "*java" and "a*b" do not. |
|
* <P> |
|
* The actions to be granted are passed to the constructor in a string containing |
|
* a list of one or more comma-separated keywords. The possible keywords are |
|
* "read" and "write". Their meaning is defined as follows: |
|
* |
|
* <DL> |
|
* <DT> read |
|
* <DD> read permission. Allows {@code System.getProperty} to |
|
* be called. |
|
* <DT> write |
|
* <DD> write permission. Allows {@code System.setProperty} to |
|
* be called. |
|
* </DL> |
|
* <P> |
|
* The actions string is converted to lowercase before processing. |
|
* <P> |
|
* Care should be taken before granting code permission to access |
|
* certain system properties. For example, granting permission to |
|
* access the "java.home" system property gives potentially malevolent |
|
* code sensitive information about the system environment (the Java |
|
* installation directory). Also, granting permission to access |
|
* the "user.name" and "user.home" system properties gives potentially |
|
* malevolent code sensitive information about the user environment |
|
* (the user's account name and home directory). |
|
* |
|
* @see java.security.BasicPermission |
|
* @see java.security.Permission |
|
* @see java.security.Permissions |
|
* @see java.security.PermissionCollection |
|
* @see java.lang.SecurityManager |
|
* |
|
* |
|
* @author Roland Schemers |
|
* @since 1.2 |
|
* |
|
* @serial exclude |
|
*/ |
|
|
|
public final class PropertyPermission extends BasicPermission { |
|
|
|
|
|
|
|
*/ |
|
private static final int READ = 0x1; |
|
|
|
|
|
|
|
*/ |
|
private static final int WRITE = 0x2; |
|
|
|
|
|
*/ |
|
private static final int ALL = READ|WRITE; |
|
|
|
|
|
*/ |
|
private static final int NONE = 0x0; |
|
|
|
|
|
|
|
|
|
*/ |
|
private transient int mask; |
|
|
|
/** |
|
* The actions string. |
|
* |
|
* @serial |
|
*/ |
|
private String actions; |
|
// created and re-used in the getAction function. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void init(int mask) { |
|
if ((mask & ALL) != mask) |
|
throw new IllegalArgumentException("invalid actions mask"); |
|
|
|
if (mask == NONE) |
|
throw new IllegalArgumentException("invalid actions mask"); |
|
|
|
if (getName() == null) |
|
throw new NullPointerException("name can't be null"); |
|
|
|
this.mask = mask; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public PropertyPermission(String name, String actions) { |
|
super(name,actions); |
|
init(getMask(actions)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
PropertyPermission(String name, int mask) { |
|
super(name, getActions(mask)); |
|
this.mask = mask; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public boolean implies(Permission p) { |
|
// we get the effective mask. i.e., the "and" of this and that. |
|
|
|
return p instanceof PropertyPermission that |
|
&& ((this.mask & that.mask) == that.mask) |
|
&& super.implies(that); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public boolean equals(Object obj) { |
|
if (obj == this) |
|
return true; |
|
|
|
return obj instanceof PropertyPermission that |
|
&& this.mask == that.mask |
|
&& this.getName().equals(that.getName()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public int hashCode() { |
|
return this.getName().hashCode(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static int getMask(String actions) { |
|
|
|
int mask = NONE; |
|
|
|
if (actions == null) { |
|
return mask; |
|
} |
|
|
|
// Use object identity comparison against known-interned strings for |
|
|
|
if (actions == SecurityConstants.PROPERTY_READ_ACTION) { |
|
return READ; |
|
} if (actions == SecurityConstants.PROPERTY_WRITE_ACTION) { |
|
return WRITE; |
|
} else if (actions == SecurityConstants.PROPERTY_RW_ACTION) { |
|
return READ|WRITE; |
|
} |
|
|
|
char[] a = actions.toCharArray(); |
|
|
|
int i = a.length - 1; |
|
if (i < 0) |
|
return mask; |
|
|
|
while (i != -1) { |
|
char c; |
|
|
|
|
|
while ((i!=-1) && ((c = a[i]) == ' ' || |
|
c == '\r' || |
|
c == '\n' || |
|
c == '\f' || |
|
c == '\t')) |
|
i--; |
|
|
|
|
|
int matchlen; |
|
|
|
if (i >= 3 && (a[i-3] == 'r' || a[i-3] == 'R') && |
|
(a[i-2] == 'e' || a[i-2] == 'E') && |
|
(a[i-1] == 'a' || a[i-1] == 'A') && |
|
(a[i] == 'd' || a[i] == 'D')) |
|
{ |
|
matchlen = 4; |
|
mask |= READ; |
|
|
|
} else if (i >= 4 && (a[i-4] == 'w' || a[i-4] == 'W') && |
|
(a[i-3] == 'r' || a[i-3] == 'R') && |
|
(a[i-2] == 'i' || a[i-2] == 'I') && |
|
(a[i-1] == 't' || a[i-1] == 'T') && |
|
(a[i] == 'e' || a[i] == 'E')) |
|
{ |
|
matchlen = 5; |
|
mask |= WRITE; |
|
|
|
} else { |
|
|
|
throw new IllegalArgumentException( |
|
"invalid permission: " + actions); |
|
} |
|
|
|
// make sure we didn't just match the tail of a word |
|
|
|
boolean seencomma = false; |
|
while (i >= matchlen && !seencomma) { |
|
switch(a[i-matchlen]) { |
|
case ',': |
|
seencomma = true; |
|
break; |
|
case ' ': case '\r': case '\n': |
|
case '\f': case '\t': |
|
break; |
|
default: |
|
throw new IllegalArgumentException( |
|
"invalid permission: " + actions); |
|
} |
|
i--; |
|
} |
|
|
|
|
|
i -= matchlen; |
|
} |
|
|
|
return mask; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static String getActions(int mask) { |
|
return switch (mask & (READ | WRITE)) { |
|
case READ -> SecurityConstants.PROPERTY_READ_ACTION; |
|
case WRITE -> SecurityConstants.PROPERTY_WRITE_ACTION; |
|
case READ | WRITE -> SecurityConstants.PROPERTY_RW_ACTION; |
|
default -> ""; |
|
}; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public String getActions() { |
|
if (actions == null) |
|
actions = getActions(this.mask); |
|
|
|
return actions; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
int getMask() { |
|
return mask; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public PermissionCollection newPermissionCollection() { |
|
return new PropertyPermissionCollection(); |
|
} |
|
|
|
@java.io.Serial |
|
private static final long serialVersionUID = 885438825399942851L; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@java.io.Serial |
|
private synchronized void writeObject(java.io.ObjectOutputStream s) |
|
throws IOException |
|
{ |
|
// Write out the actions. The superclass takes care of the name |
|
|
|
if (actions == null) |
|
getActions(); |
|
s.defaultWriteObject(); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@java.io.Serial |
|
private synchronized void readObject(java.io.ObjectInputStream s) |
|
throws IOException, ClassNotFoundException |
|
{ |
|
|
|
s.defaultReadObject(); |
|
init(getMask(actions)); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
final class PropertyPermissionCollection extends PermissionCollection |
|
implements Serializable |
|
{ |
|
|
|
|
|
|
|
|
|
*/ |
|
private transient ConcurrentHashMap<String, PropertyPermission> perms; |
|
|
|
/** |
|
* Boolean saying if "*" is in the collection. |
|
* |
|
* @see #serialPersistentFields |
|
*/ |
|
|
|
private boolean all_allowed; |
|
|
|
|
|
|
|
*/ |
|
public PropertyPermissionCollection() { |
|
perms = new ConcurrentHashMap<>(32); |
|
all_allowed = false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void add(Permission permission) { |
|
if (! (permission instanceof PropertyPermission pp)) |
|
throw new IllegalArgumentException("invalid permission: "+ |
|
permission); |
|
if (isReadOnly()) |
|
throw new SecurityException( |
|
"attempt to add a Permission to a readonly PermissionCollection"); |
|
|
|
String propName = pp.getName(); |
|
|
|
// Add permission to map if it is absent, or replace with new |
|
// permission if applicable. NOTE: cannot use lambda for |
|
|
|
perms.merge(propName, pp, |
|
new java.util.function.BiFunction<>() { |
|
@Override |
|
public PropertyPermission apply(PropertyPermission existingVal, |
|
PropertyPermission newVal) { |
|
|
|
int oldMask = existingVal.getMask(); |
|
int newMask = newVal.getMask(); |
|
if (oldMask != newMask) { |
|
int effective = oldMask | newMask; |
|
if (effective == newMask) { |
|
return newVal; |
|
} |
|
if (effective != oldMask) { |
|
return new PropertyPermission(propName, effective); |
|
} |
|
} |
|
return existingVal; |
|
} |
|
} |
|
); |
|
|
|
if (!all_allowed) { |
|
if (propName.equals("*")) |
|
all_allowed = true; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public boolean implies(Permission permission) { |
|
if (! (permission instanceof PropertyPermission pp)) |
|
return false; |
|
|
|
PropertyPermission x; |
|
|
|
int desired = pp.getMask(); |
|
int effective = 0; |
|
|
|
|
|
if (all_allowed) { |
|
x = perms.get("*"); |
|
if (x != null) { |
|
effective |= x.getMask(); |
|
if ((effective & desired) == desired) |
|
return true; |
|
} |
|
} |
|
|
|
// strategy: |
|
// Check for full match first. Then work our way up the |
|
// name looking for matches on a.b.* |
|
|
|
String name = pp.getName(); |
|
//System.out.println("check "+name); |
|
|
|
x = perms.get(name); |
|
|
|
if (x != null) { |
|
|
|
effective |= x.getMask(); |
|
if ((effective & desired) == desired) |
|
return true; |
|
} |
|
|
|
|
|
int last, offset; |
|
|
|
offset = name.length()-1; |
|
|
|
while ((last = name.lastIndexOf('.', offset)) != -1) { |
|
|
|
name = name.substring(0, last+1) + "*"; |
|
|
|
x = perms.get(name); |
|
|
|
if (x != null) { |
|
effective |= x.getMask(); |
|
if ((effective & desired) == desired) |
|
return true; |
|
} |
|
offset = last -1; |
|
} |
|
|
|
// we don't have to check for "*" as it was already checked |
|
|
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
@SuppressWarnings("unchecked") |
|
public Enumeration<Permission> elements() { |
|
|
|
|
|
|
|
*/ |
|
return (Enumeration)perms.elements(); |
|
} |
|
|
|
@java.io.Serial |
|
private static final long serialVersionUID = 7015263904581634791L; |
|
|
|
// Need to maintain serialization interoperability with earlier releases, |
|
// which had the serializable field: |
|
// |
|
// Table of permissions. |
|
// |
|
// @serial |
|
// |
|
// private Hashtable permissions; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static final ObjectStreamField[] serialPersistentFields = { |
|
new ObjectStreamField("permissions", Hashtable.class), |
|
new ObjectStreamField("all_allowed", Boolean.TYPE), |
|
}; |
|
|
|
/** |
|
* @serialData Default fields. |
|
*/ |
|
|
|
|
|
|
|
|
|
*/ |
|
@java.io.Serial |
|
private void writeObject(ObjectOutputStream out) throws IOException { |
|
// Don't call out.defaultWriteObject() |
|
|
|
|
|
Hashtable<String, Permission> permissions = |
|
new Hashtable<>(perms.size()*2); |
|
permissions.putAll(perms); |
|
|
|
|
|
ObjectOutputStream.PutField pfields = out.putFields(); |
|
pfields.put("all_allowed", all_allowed); |
|
pfields.put("permissions", permissions); |
|
out.writeFields(); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@java.io.Serial |
|
private void readObject(ObjectInputStream in) |
|
throws IOException, ClassNotFoundException |
|
{ |
|
// Don't call defaultReadObject() |
|
|
|
|
|
ObjectInputStream.GetField gfields = in.readFields(); |
|
|
|
|
|
all_allowed = gfields.get("all_allowed", false); |
|
|
|
|
|
@SuppressWarnings("unchecked") |
|
Hashtable<String, PropertyPermission> permissions = |
|
(Hashtable<String, PropertyPermission>)gfields.get("permissions", null); |
|
perms = new ConcurrentHashMap<>(permissions.size()*2); |
|
perms.putAll(permissions); |
|
} |
|
} |