Path: blob/master/src/java.base/share/classes/java/security/Permissions.java
41152 views
/*1* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.security;2627import java.io.InvalidObjectException;28import java.io.IOException;29import java.io.ObjectInputStream;30import java.io.ObjectOutputStream;31import java.io.ObjectStreamField;32import java.io.Serializable;33import java.util.Enumeration;34import java.util.Hashtable;35import java.util.Iterator;36import java.util.List;37import java.util.Map;38import java.util.NoSuchElementException;39import java.util.concurrent.ConcurrentHashMap;4041/**42* This class represents a heterogeneous collection of Permissions. That is,43* it contains different types of Permission objects, organized into44* PermissionCollections. For example, if any45* {@code java.io.FilePermission} objects are added to an instance of46* this class, they are all stored in a single47* PermissionCollection. It is the PermissionCollection returned by a call to48* the {@code newPermissionCollection} method in the FilePermission class.49* Similarly, any {@code java.lang.RuntimePermission} objects are50* stored in the PermissionCollection returned by a call to the51* {@code newPermissionCollection} method in the52* RuntimePermission class. Thus, this class represents a collection of53* PermissionCollections.54*55* <p>When the {@code add} method is called to add a Permission, the56* Permission is stored in the appropriate PermissionCollection. If no such57* collection exists yet, the Permission object's class is determined and the58* {@code newPermissionCollection} method is called on that class to create59* the PermissionCollection and add it to the Permissions object. If60* {@code newPermissionCollection} returns null, then a default61* PermissionCollection that uses a hashtable will be created and used. Each62* hashtable entry stores a Permission object as both the key and the value.63*64* <p> Enumerations returned via the {@code elements} method are65* not <em>fail-fast</em>. Modifications to a collection should not be66* performed while enumerating over that collection.67*68* @see Permission69* @see PermissionCollection70* @see AllPermission71*72*73* @author Marianne Mueller74* @author Roland Schemers75* @since 1.276*77* @serial exclude78*/7980public final class Permissions extends PermissionCollection81implements Serializable82{83/**84* Key is permissions Class, value is PermissionCollection for that class.85* Not serialized; see serialization section at end of class.86*/87private transient ConcurrentHashMap<Class<?>, PermissionCollection> permsMap;8889// optimization. keep track of whether unresolved permissions need to be90// checked91private transient boolean hasUnresolved = false;9293// optimization. keep track of the AllPermission collection94// - package private for ProtectionDomain optimization95PermissionCollection allPermission;9697/**98* Creates a new Permissions object containing no PermissionCollections.99*/100public Permissions() {101permsMap = new ConcurrentHashMap<>(11);102allPermission = null;103}104105/**106* Adds a permission object to the PermissionCollection for the class the107* permission belongs to. For example, if <i>permission</i> is a108* FilePermission, it is added to the FilePermissionCollection stored109* in this Permissions object.110*111* This method creates112* a new PermissionCollection object (and adds the permission to it)113* if an appropriate collection does not yet exist.114*115* @param permission the Permission object to add.116*117* @throws SecurityException if this Permissions object is118* marked as readonly.119*120* @see PermissionCollection#isReadOnly()121*/122@Override123public void add(Permission permission) {124if (isReadOnly())125throw new SecurityException(126"attempt to add a Permission to a readonly Permissions object");127128PermissionCollection pc = getPermissionCollection(permission, true);129pc.add(permission);130131// No sync; staleness -> optimizations delayed, which is OK132if (permission instanceof AllPermission) {133allPermission = pc;134}135if (permission instanceof UnresolvedPermission) {136hasUnresolved = true;137}138}139140/**141* Checks to see if this object's PermissionCollection for permissions of142* the specified permission's class implies the permissions143* expressed in the <i>permission</i> object. Returns true if the144* combination of permissions in the appropriate PermissionCollection145* (e.g., a FilePermissionCollection for a FilePermission) together146* imply the specified permission.147*148* <p>For example, suppose there is a FilePermissionCollection in this149* Permissions object, and it contains one FilePermission that specifies150* "read" access for all files in all subdirectories of the "/tmp"151* directory, and another FilePermission that specifies "write" access152* for all files in the "/tmp/scratch/foo" directory.153* Then if the {@code implies} method154* is called with a permission specifying both "read" and "write" access155* to files in the "/tmp/scratch/foo" directory, {@code true} is156* returned.157*158* <p>Additionally, if this PermissionCollection contains the159* AllPermission, this method will always return true.160*161* @param permission the Permission object to check.162*163* @return true if "permission" is implied by the permissions in the164* PermissionCollection it165* belongs to, false if not.166*/167@Override168public boolean implies(Permission permission) {169// No sync; staleness -> skip optimization, which is OK170if (allPermission != null) {171return true; // AllPermission has already been added172} else {173PermissionCollection pc = getPermissionCollection(permission,174false);175if (pc != null) {176return pc.implies(permission);177} else {178// none found179return false;180}181}182}183184/**185* Returns an enumeration of all the Permission objects in all the186* PermissionCollections in this Permissions object.187*188* @return an enumeration of all the Permissions.189*/190@Override191public Enumeration<Permission> elements() {192// go through each Permissions in the hash table193// and call their elements() function.194195return new PermissionsEnumerator(permsMap.values().iterator());196}197198/**199* Gets the PermissionCollection in this Permissions object for200* permissions whose type is the same as that of <i>p</i>.201* For example, if <i>p</i> is a FilePermission,202* the FilePermissionCollection203* stored in this Permissions object will be returned.204*205* If createEmpty is true,206* this method creates a new PermissionCollection object for the specified207* type of permission objects if one does not yet exist.208* To do so, it first calls the {@code newPermissionCollection} method209* on <i>p</i>. Subclasses of class Permission210* override that method if they need to store their permissions in a211* particular PermissionCollection object in order to provide the212* correct semantics when the {@code PermissionCollection.implies}213* method is called.214* If the call returns a PermissionCollection, that collection is stored215* in this Permissions object. If the call returns null and createEmpty216* is true, then217* this method instantiates and stores a default PermissionCollection218* that uses a hashtable to store its permission objects.219*220* createEmpty is ignored when creating empty PermissionCollection221* for unresolved permissions because of the overhead of determining the222* PermissionCollection to use.223*224* createEmpty should be set to false when this method is invoked from225* implies() because it incurs the additional overhead of creating and226* adding an empty PermissionCollection that will just return false.227* It should be set to true when invoked from add().228*/229private PermissionCollection getPermissionCollection(Permission p,230boolean createEmpty) {231PermissionCollection pc = permsMap.get(p.getClass());232if ((!hasUnresolved && !createEmpty) || pc != null) {233// Collection not to be created, or already created234return pc;235}236return createPermissionCollection(p, createEmpty);237}238239private PermissionCollection createPermissionCollection(Permission p,240boolean createEmpty) {241synchronized (permsMap) {242// Re-read under lock243Class<?> c = p.getClass();244PermissionCollection pc = permsMap.get(c);245246// Collection already created247if (pc != null) {248return pc;249}250251// Create and add permission collection to map if it is absent.252// Check for unresolved permissions253pc = (hasUnresolved ? getUnresolvedPermissions(p) : null);254255// if still null, create a new collection256if (pc == null && createEmpty) {257258pc = p.newPermissionCollection();259260// still no PermissionCollection?261// We'll give them a PermissionsHash.262if (pc == null) {263pc = new PermissionsHash();264}265}266if (pc != null) {267// Add pc, resolving any race268PermissionCollection oldPc = permsMap.putIfAbsent(c, pc);269if (oldPc != null) {270pc = oldPc;271}272}273return pc;274}275}276277/**278* Resolves any unresolved permissions of type p.279*280* @param p the type of unresolved permission to resolve281*282* @return PermissionCollection containing the unresolved permissions,283* or null if there were no unresolved permissions of type p.284*285*/286private PermissionCollection getUnresolvedPermissions(Permission p)287{288UnresolvedPermissionCollection uc =289(UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class);290291// we have no unresolved permissions if uc is null292if (uc == null)293return null;294295List<UnresolvedPermission> unresolvedPerms =296uc.getUnresolvedPermissions(p);297298// we have no unresolved permissions of this type if unresolvedPerms is null299if (unresolvedPerms == null)300return null;301302java.security.cert.Certificate[] certs = null;303304Object[] signers = p.getClass().getSigners();305306int n = 0;307if (signers != null) {308for (int j=0; j < signers.length; j++) {309if (signers[j] instanceof java.security.cert.Certificate) {310n++;311}312}313certs = new java.security.cert.Certificate[n];314n = 0;315for (int j=0; j < signers.length; j++) {316if (signers[j] instanceof java.security.cert.Certificate) {317certs[n++] = (java.security.cert.Certificate)signers[j];318}319}320}321322PermissionCollection pc = null;323synchronized (unresolvedPerms) {324int len = unresolvedPerms.size();325for (int i = 0; i < len; i++) {326UnresolvedPermission up = unresolvedPerms.get(i);327Permission perm = up.resolve(p, certs);328if (perm != null) {329if (pc == null) {330pc = p.newPermissionCollection();331if (pc == null)332pc = new PermissionsHash();333}334pc.add(perm);335}336}337}338return pc;339}340341@java.io.Serial342private static final long serialVersionUID = 4858622370623524688L;343344// Need to maintain serialization interoperability with earlier releases,345// which had the serializable field:346// private Hashtable perms;347348/**349* @serialField perms java.util.Hashtable350* A table of the Permission classes and PermissionCollections.351* @serialField allPermission java.security.PermissionCollection352*/353@java.io.Serial354private static final ObjectStreamField[] serialPersistentFields = {355new ObjectStreamField("perms", Hashtable.class),356new ObjectStreamField("allPermission", PermissionCollection.class),357};358359/**360* @serialData Default fields.361*/362/*363* Writes the contents of the permsMap field out as a Hashtable for364* serialization compatibility with earlier releases. allPermission365* unchanged.366*/367@java.io.Serial368private void writeObject(ObjectOutputStream out) throws IOException {369// Don't call out.defaultWriteObject()370371// Copy perms into a Hashtable372Hashtable<Class<?>, PermissionCollection> perms =373new Hashtable<>(permsMap.size()*2); // no sync; estimate374perms.putAll(permsMap);375376// Write out serializable fields377ObjectOutputStream.PutField pfields = out.putFields();378379pfields.put("allPermission", allPermission); // no sync; staleness OK380pfields.put("perms", perms);381out.writeFields();382}383384/*385* Reads in a Hashtable of Class/PermissionCollections and saves them in the386* permsMap field. Reads in allPermission.387*/388@java.io.Serial389private void readObject(ObjectInputStream in) throws IOException,390ClassNotFoundException {391// Don't call defaultReadObject()392393// Read in serialized fields394ObjectInputStream.GetField gfields = in.readFields();395396// Get allPermission397allPermission = (PermissionCollection) gfields.get("allPermission", null);398399// Get permissions400// writeObject writes a Hashtable<Class<?>, PermissionCollection> for401// the perms key, so this cast is safe, unless the data is corrupt.402@SuppressWarnings("unchecked")403Hashtable<Class<?>, PermissionCollection> perms =404(Hashtable<Class<?>, PermissionCollection>)gfields.get("perms", null);405permsMap = new ConcurrentHashMap<>(perms.size()*2);406permsMap.putAll(perms);407408// Check that Class is mapped to PermissionCollection containing409// Permissions of the same class410for (Map.Entry<Class<?>, PermissionCollection> e : perms.entrySet()) {411Class<?> k = e.getKey();412PermissionCollection v = e.getValue();413Enumeration<Permission> en = v.elements();414while (en.hasMoreElements()) {415Permission p = en.nextElement();416if (!k.equals(p.getClass())) {417throw new InvalidObjectException("Permission with class " +418k + " incorrectly mapped to PermissionCollection " +419"containing Permission with " + p.getClass());420}421}422}423424// Set hasUnresolved425UnresolvedPermissionCollection uc =426(UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class);427hasUnresolved = (uc != null && uc.elements().hasMoreElements());428}429}430431final class PermissionsEnumerator implements Enumeration<Permission> {432433// all the perms434private Iterator<PermissionCollection> perms;435// the current set436private Enumeration<Permission> permset;437438PermissionsEnumerator(Iterator<PermissionCollection> e) {439perms = e;440permset = getNextEnumWithMore();441}442443// No need to synchronize; caller should sync on object as required444public boolean hasMoreElements() {445// if we enter with permissionimpl null, we know446// there are no more left.447448if (permset == null)449return false;450451// try to see if there are any left in the current one452453if (permset.hasMoreElements())454return true;455456// get the next one that has something in it...457permset = getNextEnumWithMore();458459// if it is null, we are done!460return (permset != null);461}462463// No need to synchronize; caller should sync on object as required464public Permission nextElement() {465466// hasMoreElements will update permset to the next permset467// with something in it...468469if (hasMoreElements()) {470return permset.nextElement();471} else {472throw new NoSuchElementException("PermissionsEnumerator");473}474475}476477private Enumeration<Permission> getNextEnumWithMore() {478while (perms.hasNext()) {479PermissionCollection pc = perms.next();480Enumeration<Permission> next =pc.elements();481if (next.hasMoreElements())482return next;483}484return null;485486}487}488489/**490* A PermissionsHash stores a homogeneous set of permissions in a hashtable.491*492* @see Permission493* @see Permissions494*495*496* @author Roland Schemers497*498* @serial include499*/500501final class PermissionsHash extends PermissionCollection502implements Serializable503{504/**505* Key and value are (same) permissions objects.506* Not serialized; see serialization section at end of class.507*/508private transient ConcurrentHashMap<Permission, Permission> permsMap;509510/**511* Create an empty PermissionsHash object.512*/513PermissionsHash() {514permsMap = new ConcurrentHashMap<>(11);515}516517/**518* Adds a permission to the PermissionsHash.519*520* @param permission the Permission object to add.521*/522@Override523public void add(Permission permission) {524permsMap.put(permission, permission);525}526527/**528* Check and see if this set of permissions implies the permissions529* expressed in "permission".530*531* @param permission the Permission object to compare532*533* @return true if "permission" is a proper subset of a permission in534* the set, false if not.535*/536@Override537public boolean implies(Permission permission) {538// attempt a fast lookup and implies. If that fails539// then enumerate through all the permissions.540Permission p = permsMap.get(permission);541542// If permission is found, then p.equals(permission)543if (p == null) {544for (Permission p_ : permsMap.values()) {545if (p_.implies(permission))546return true;547}548return false;549} else {550return true;551}552}553554/**555* Returns an enumeration of all the Permission objects in the container.556*557* @return an enumeration of all the Permissions.558*/559@Override560public Enumeration<Permission> elements() {561return permsMap.elements();562}563564@java.io.Serial565private static final long serialVersionUID = -8491988220802933440L;566// Need to maintain serialization interoperability with earlier releases,567// which had the serializable field:568// private Hashtable perms;569/**570* @serialField perms java.util.Hashtable571* A table of the Permissions (both key and value are same).572*/573@java.io.Serial574private static final ObjectStreamField[] serialPersistentFields = {575new ObjectStreamField("perms", Hashtable.class),576};577578/**579* Writes the contents of the permsMap field out as a Hashtable for580* serialization compatibility with earlier releases.581*582* @param out the {@code ObjectOutputStream} to which data is written583* @throws IOException if an I/O error occurs584*/585@java.io.Serial586private void writeObject(ObjectOutputStream out) throws IOException {587// Don't call out.defaultWriteObject()588589// Copy perms into a Hashtable590Hashtable<Permission, Permission> perms =591new Hashtable<>(permsMap.size()*2);592perms.putAll(permsMap);593594// Write out serializable fields595ObjectOutputStream.PutField pfields = out.putFields();596pfields.put("perms", perms);597out.writeFields();598}599600/**601* Reads in a Hashtable of Permission/Permission and saves them in the602* permsMap field.603*604* @param in the {@code ObjectInputStream} from which data is read605* @throws IOException if an I/O error occurs606* @throws ClassNotFoundException if a serialized class cannot be loaded607*/608@java.io.Serial609private void readObject(ObjectInputStream in) throws IOException,610ClassNotFoundException {611// Don't call defaultReadObject()612613// Read in serialized fields614ObjectInputStream.GetField gfields = in.readFields();615616// Get permissions617// writeObject writes a Hashtable<Class<?>, PermissionCollection> for618// the perms key, so this cast is safe, unless the data is corrupt.619@SuppressWarnings("unchecked")620Hashtable<Permission, Permission> perms =621(Hashtable<Permission, Permission>)gfields.get("perms", null);622permsMap = new ConcurrentHashMap<>(perms.size()*2);623permsMap.putAll(perms);624625// check that the Permission key and value are the same object626for (Map.Entry<Permission, Permission> e : perms.entrySet()) {627Permission k = e.getKey();628Permission v = e.getValue();629if (k != v) {630throw new InvalidObjectException("Permission (" + k +631") incorrectly mapped to Permission (" + v + ")");632}633}634}635}636637638