/* * @(#)Permissions.java 1.54 06/10/11 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. * */ package java.security; import java.util.Enumeration; import java.util.Hashtable; import java.util.NoSuchElementException; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.ArrayList; import java.util.Iterator; import java.util.Collections; import java.io.Serializable; import java.io.ObjectStreamField; import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.io.IOException; /** * This class represents a heterogeneous collection of Permissions. That is, * it contains different types of Permission objects, organized into * PermissionCollections. For example, if any * <code>java.io.FilePermission</code> objects are added to an instance of * this class, they are all stored in a single * PermissionCollection. It is the PermissionCollection returned by a call to * the <code>newPermissionCollection</code> method in the FilePermission class. * Similarly, any <code>java.lang.RuntimePermission</code> objects are * stored in the PermissionCollection returned by a call to the * <code>newPermissionCollection</code> method in the * RuntimePermission class. Thus, this class represents a collection of * PermissionCollections. * * <p>When the <code>add</code> method is called to add a Permission, the * Permission is stored in the appropriate PermissionCollection. If no such * collection exists yet, the Permission object's class is determined and the * <code>newPermissionCollection</code> method is called on that class to create * the PermissionCollection and add it to the Permissions object. If * <code>newPermissionCollection</code> returns null, then a default * PermissionCollection that uses a hashtable will be created and used. Each * hashtable entry stores a Permission object as both the key and the value. * * <p> Enumerations returned via the <code>elements</code> method are * not <em>fail-fast</em>. Modifications to a collection should not be * performed while enumerating over that collection. * * @see Permission * @see PermissionCollection * @see AllPermission * * @version 1.46, 00/02/02 * * @author Marianne Mueller * @author Roland Schemers * * @serial exclude */ public final class Permissions extends PermissionCollection implements Serializable { /** * Key is permissions Class, value is PermissionCollection for that class. * Not serialized; see serialization section at end of class. */ private transient Map permsMap; // optimization. keep track of whether unresolved permissions need to be checked private transient boolean hasUnresolved = false; // optimization. keep track of the AllPermission collection private PermissionCollection allPermission; /** * Creates a new Permissions object containing no PermissionCollections. */ public Permissions() { permsMap = new HashMap(11); allPermission = null; } /** * Adds a permission object to the PermissionCollection for the class the * permission belongs to. For example, if <i>permission</i> is a * FilePermission, it is added to the FilePermissionCollection stored * in this Permissions object. * * This method creates * a new PermissionCollection object (and adds the permission to it) * if an appropriate collection does not yet exist. <p> * * @param permission the Permission object to add. * * @exception SecurityException if this Permissions object is * marked as readonly. * * @see PermissionCollection#isReadOnly() */ public void add(Permission permission) { if (isReadOnly()) throw new SecurityException( "attempt to add a Permission to a readonly Permissions object"); PermissionCollection pc = getPermissionCollection(permission, true); pc.add(permission); // No need to synchronize because all adds are done sequentially // and allPermission and hasUnresolved are set only by add() if (permission instanceof AllPermission) { allPermission = pc; } if (permission instanceof UnresolvedPermission) { hasUnresolved = true; } } /** * Checks to see if this object's PermissionCollection for permissions of * the specified permission's type implies the permissions * expressed in the <i>permission</i> object. Returns true if the * combination of permissions in the appropriate PermissionCollection * (e.g., a FilePermissionCollection for a FilePermission) together * imply the specified permission. * * <p>For example, suppose there is a FilePermissionCollection in this * Permissions object, and it contains one FilePermission that specifies * "read" access for all files in all subdirectories of the "/tmp" * directory, and another FilePermission that specifies "write" access * for all files in the "/tmp/scratch/foo" directory. * Then if the <code>implies</code> method * is called with a permission specifying both "read" and "write" access * to files in the "/tmp/scratch/foo" directory, <code>true</code> is * returned. * * <p>Additionally, if this PermissionCollection contains the * AllPermission, this method will always return true. * <p> * @param permission the Permission object to check. * * @return true if "permission" is implied by the permissions in the * PermissionCollection it * belongs to, false if not. */ public boolean implies(Permission permission) { if (allPermission != null) { return true; // AllPermission has already been added } else { PermissionCollection pc = getPermissionCollection(permission, false); if (pc != null) { return pc.implies(permission); } else { // none found return false; } } } /** * Returns an enumeration of all the Permission objects in all the * PermissionCollections in this Permissions object. * * @return an enumeration of all the Permissions. */ public Enumeration elements() { // go through each Permissions in the hash table // and call their elements() function. if (hasUnresolved) { // Take snapshot of what's in permsMap because permsMap might // change during iteration by implies() of other threads; // individual PermissionCollections won't change during implies(). List copy = new ArrayList(); synchronized (permsMap) { copy.addAll(permsMap.values()); } return new PermissionsEnumerator(copy.iterator()); } else { // permsMap won't be updated during iteration; sync unnecessary return new PermissionsEnumerator(permsMap.values().iterator()); } } /** * Gets the PermissionCollection in this Permissions object for * permissions whose type is the same as that of <i>p</i>. * For example, if <i>p</i> is a FilePermission, * the FilePermissionCollection * stored in this Permissions object will be returned. * * If createEmtpy is true, * this method creates a new PermissionCollection object for the specified * type of permission objects if one does not yet exist. * To do so, it first calls the <code>newPermissionCollection</code> method * on <i>p</i>. Subclasses of class Permission * override that method if they need to store their permissions in a * particular PermissionCollection object in order to provide the * correct semantics when the <code>PermissionCollection.implies</code> * method is called. * If the call returns a PermissionCollection, that collection is stored * in this Permissions object. If the call returns null and createEmpty * is true, then * this method instantiates and stores a default PermissionCollection * that uses a hashtable to store its permission objects. * * createEmtpy is ignored when creating empty PermissionCollection * for unresolved permissions because of the overhead of determining the * PermissionCollection to use. * * createEmpty should be set to false when this method is invoked from * implies() because it incurs the additional overhead of creating and * adding an empty PermissionCollection that will just return false. * It should be set to true when invoked from add(). */ private PermissionCollection getPermissionCollection(Permission p, boolean createEmpty) { Class c = p.getClass(); if (!hasUnresolved && !createEmpty) { // No need to synchronize because updates have all been done return (PermissionCollection) permsMap.get(c); } else { PermissionCollection pc = null; // synchronize because permsMap might be updated with a new // PermissionCollection synchronized (permsMap) { pc = (PermissionCollection) permsMap.get(c); // Check for unresolved permissions if (pc == null) { pc = (hasUnresolved ? getUnresolvedPermissions(p) : null); // if still null, create a new collection if (pc == null && createEmpty) { pc = p.newPermissionCollection(); // still no PermissionCollection? // We'll give them a PermissionsHash. if (pc == null) pc = new PermissionsHash(); } if (pc != null) { permsMap.put(c, pc); } } } return pc; } } /** * Resolves any unresolved permissions of type p. * * @param p the type of unresolved permission to resolve * * @return PermissionCollection containing the unresolved permissions, * or null if there were no unresolved permissions of type p. * */ private PermissionCollection getUnresolvedPermissions(Permission p) { // Called from within synchronized method so permsMap doesn't need lock UnresolvedPermissionCollection uc = (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class); // we have no unresolved permissions if uc is null if (uc == null) return null; List unresolvedPerms = uc.getUnresolvedPermissions(p); // we have no unresolved permissions of this type if unresolvedPerms is null if (unresolvedPerms == null) return null; java.security.cert.Certificate certs[] = null; Object signers[] = p.getClass().getSigners(); int n = 0; if (signers != null) { for (int j=0; j < signers.length; j++) { if (signers[j] instanceof java.security.cert.Certificate) { n++; } } certs = new java.security.cert.Certificate[n]; n = 0; for (int j=0; j < signers.length; j++) { if (signers[j] instanceof java.security.cert.Certificate) { certs[n++] = (java.security.cert.Certificate)signers[j]; } } } PermissionCollection pc = null; int len = unresolvedPerms.size(); for (int i = 0; i < len; i++) { UnresolvedPermission up = (UnresolvedPermission)unresolvedPerms.get(i); Permission perm = up.resolve(p, certs); if (perm != null) { if (pc == null) { pc = p.newPermissionCollection(); if (pc == null) pc = new PermissionsHash(); } pc.add(perm); } } return pc; } private static final long serialVersionUID = 4858622370623524688L; // Need to maintain serialization interoperability with earlier releases, // which had the serializable field: // private Hashtable perms; /** * @serialField perms java.util.Hashtable * A table of the Permission classes and PermissionCollections. * @serialField allPermission java.security.PermissionCollection */ private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("perms", Hashtable.class), new ObjectStreamField("allPermission", PermissionCollection.class), }; /** * @serialData Default fields. */ /* * Writes the contents of the permsMap field out as a Hashtable for * serialization compatibility with earlier releases. allPermission * unchanged. */ private void writeObject(ObjectOutputStream out) throws IOException { // Don't call out.defaultWriteObject() // Copy perms into a Hashtable Hashtable perms = new Hashtable(permsMap.size()*2); perms.putAll(permsMap); // Write out serializable fields ObjectOutputStream.PutField pfields = out.putFields(); pfields.put("allPermission", allPermission); pfields.put("perms", perms); out.writeFields(); } /* * Reads in a Hashtable of Class/PermissionCollections and saves them in the * permsMap field. Reads in allPermission. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Don't call defaultReadObject() // Read in serialized fields ObjectInputStream.GetField gfields = in.readFields(); // Get allPermission allPermission = (PermissionCollection) gfields.get("allPermission", null); // Get permissions Hashtable perms = (Hashtable)gfields.get("perms", null); permsMap = new HashMap(perms.size()*2); permsMap.putAll(perms); // Set hasUnresolved UnresolvedPermissionCollection uc = (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class); hasUnresolved = (uc != null && uc.elements().hasMoreElements()); } } final class PermissionsEnumerator implements Enumeration { // all the perms private Iterator perms; // the current set private Enumeration permset; PermissionsEnumerator(Iterator e) { perms = e; permset = getNextEnumWithMore(); } // No need to synchronize; caller should sync on object as required public boolean hasMoreElements() { // if we enter with permissionimpl null, we know // there are no more left. if (permset == null) return false; // try to see if there are any left in the current one if (permset.hasMoreElements()) return true; // get the next one that has something in it... permset = getNextEnumWithMore(); // if it is null, we are done! return (permset != null); } // No need to synchronize; caller should sync on object as required public Object nextElement() { // hasMoreElements will update permset to the next permset // with something in it... if (hasMoreElements()) { return permset.nextElement(); } else { throw new NoSuchElementException("PermissionsEnumerator"); } } private Enumeration getNextEnumWithMore() { while (perms.hasNext()) { PermissionCollection pc = (PermissionCollection)perms.next(); Enumeration next = (Enumeration) pc.elements(); if (next.hasMoreElements()) return next; } return null; } } /** * A PermissionsHash stores a homogeneous set of permissions in a hashtable. * * @see Permission * @see Permissions * * @version 1.46, 02/02/00 * * @author Roland Schemers * * @serial include */ final class PermissionsHash extends PermissionCollection implements Serializable { /** * Key and value are (same) permissions objects. * Not serialized; see serialization section at end of class. */ private transient Map permsMap; /** * Create an empty PermissionsHash object. */ PermissionsHash() { permsMap = new HashMap(11); } /** * Adds a permission to the PermissionsHash. * * @param permission the Permission object to add. */ public void add(Permission permission) { permsMap.put(permission, permission); } /** * Check and see if this set of permissions implies the permissions * expressed in "permission". * * @param permission the Permission object to compare * * @return true if "permission" is a proper subset of a permission in * the set, false if not. */ public boolean implies(Permission permission) { // attempt a fast lookup and implies. If that fails // then enumerate through all the permissions. Permission p = (Permission) permsMap.get(permission); // If permission is found, then p.equals(permission) if (p == null) { Iterator enum_ = permsMap.values().iterator(); while (enum_.hasNext()) { p = (Permission) enum_.next(); if (p.implies(permission)) return true; } return false; } else { return true; } } /** * Returns an enumeration of all the Permission objects in the container. * * @return an enumeration of all the Permissions. */ public Enumeration elements() { // Convert Iterator of Map values into an Enumeration return Collections.enumeration(permsMap.values()); } private static final long serialVersionUID = -8491988220802933440L; // Need to maintain serialization interoperability with earlier releases, // which had the serializable field: // private Hashtable perms; /** * @serialField perms java.util.Hashtable * A table of the Permissions (both key and value are same). */ private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("perms", Hashtable.class), }; /** * @serialData Default fields. */ /* * Writes the contents of the permsMap field out as a Hashtable for * serialization compatibility with earlier releases. */ private void writeObject(ObjectOutputStream out) throws IOException { // Don't call out.defaultWriteObject() // Copy perms into a Hashtable Hashtable perms = new Hashtable(permsMap.size()*2); perms.putAll(permsMap); // Write out serializable fields ObjectOutputStream.PutField pfields = out.putFields(); pfields.put("perms", perms); out.writeFields(); } /* * Reads in a Hashtable of Permission/Permission and saves them in the * permsMap field. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Don't call defaultReadObject() // Read in serialized fields ObjectInputStream.GetField gfields = in.readFields(); // Get permissions Hashtable perms = (Hashtable)gfields.get("perms", null); permsMap = new HashMap(perms.size()*2); permsMap.putAll(perms); } }