      1 /*
      2  * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
      3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      4  *
      5  * This code is free software; you can redistribute it and/or modify it
      6  * under the terms of the GNU General Public License version 2 only, as
      7  * published by the Free Software Foundation.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle in the LICENSE file that accompanied this code.
     10  *
     11  * This code is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  * version 2 for more details (a copy is included in the LICENSE file that
     15  * accompanied this code).
     16  *
     17  * You should have received a copy of the GNU General Public License version
     18  * 2 along with this work; if not, write to the Free Software Foundation,
     19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     20  *
     21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25
     26 package java.security;
     27
     28 import java.io.InvalidObjectException;
     29 import java.io.IOException;
     30 import java.io.ObjectInputStream;
     31 import java.io.ObjectOutputStream;
     32 import java.io.ObjectStreamField;
     33 import java.io.Serializable;
     34 import java.util.Enumeration;
     35 import java.util.Hashtable;
     36 import java.util.Iterator;
     37 import java.util.List;
     38 import java.util.Map;
     39 import java.util.NoSuchElementException;
     40 import java.util.concurrent.ConcurrentHashMap;
     41
     42 /**
     43  * This class represents a heterogeneous collection of Permissions. That is,
     44  * it contains different types of Permission objects, organized into
     45  * PermissionCollections. For example, if any
     46  * {@code java.io.FilePermission} objects are added to an instance of
     47  * this class, they are all stored in a single
     48  * PermissionCollection. It is the PermissionCollection returned by a call to
     49  * the {@code newPermissionCollection} method in the FilePermission class.
     50  * Similarly, any {@code java.lang.RuntimePermission} objects are
     51  * stored in the PermissionCollection returned by a call to the
     52  * {@code newPermissionCollection} method in the
     53  * RuntimePermission class. Thus, this class represents a collection of
     54  * PermissionCollections.
     55  *
     56  * <p>When the {@code add} method is called to add a Permission, the
     57  * Permission is stored in the appropriate PermissionCollection. If no such
     58  * collection exists yet, the Permission object's class is determined and the
     59  * {@code newPermissionCollection} method is called on that class to create
     60  * the PermissionCollection and add it to the Permissions object. If
     61  * {@code newPermissionCollection} returns null, then a default
     62  * PermissionCollection that uses a hashtable will be created and used. Each
     63  * hashtable entry stores a Permission object as both the key and the value.
     64  *
     65  * <p> Enumerations returned via the {@code elements} method are
     66  * not <em>fail-fast</em>.  Modifications to a collection should not be
     67  * performed while enumerating over that collection.
     68  *
     69  * @see Permission
     70  * @see PermissionCollection
     71  * @see AllPermission
     72  *
     73  *
     74  * @author Marianne Mueller
     75  * @author Roland Schemers
     76  * @since 1.2
     77  *
     78  * @serial exclude
     79  */
     80
     81 public final class Permissions extends PermissionCollection
     82 implements Serializable
     83 {
     84     /**
     85      * Key is permissions Class, value is PermissionCollection for that class.
     86      * Not serialized; see serialization section at end of class.
     87      */
     88     private transient ConcurrentHashMap<Class<?>, PermissionCollection> permsMap;
     89
     90     // optimization. keep track of whether unresolved permissions need to be
     91     // checked
     92     private transient boolean hasUnresolved = false;
     93
     94     // optimization. keep track of the AllPermission collection
     95     // - package private for ProtectionDomain optimization
     96     PermissionCollection allPermission;
     97
     98     /**
     99      * Creates a new Permissions object containing no PermissionCollections.
    100      */
    101     public Permissions() {
    102         permsMap = new ConcurrentHashMap<>(11);
    103         allPermission = null;
    104     }
    105
    106     /**
    107      * Adds a permission object to the PermissionCollection for the class the
    108      * permission belongs to. For example, if <i>permission</i> is a
    109      * FilePermission, it is added to the FilePermissionCollection stored
    110      * in this Permissions object.
    111      *
    112      * This method creates
    113      * a new PermissionCollection object (and adds the permission to it)
    114      * if an appropriate collection does not yet exist.
    115      *
    116      * @param permission the Permission object to add.
    117      *
    118      * @throws    SecurityException if this Permissions object is
    119      * marked as readonly.
    120      *
    121      * @see PermissionCollection#isReadOnly()
    122      */
    123     @Override
    124     public void add(Permission permission) {
    125         if (isReadOnly())
    126             throw new SecurityException(
    127               "attempt to add a Permission to a readonly Permissions object");
    128
    129         PermissionCollection pc = getPermissionCollection(permission, true);
    130         pc.add(permission);
    131
    132         // No sync; staleness -> optimizations delayed, which is OK
    133         if (permission instanceof AllPermission) {
    134             allPermission = pc;
    135         }
    136         if (permission instanceof UnresolvedPermission) {
    137             hasUnresolved = true;
    138         }
    139     }
    140
    141     /**
    142      * Checks to see if this object's PermissionCollection for permissions of
    143      * the specified permission's class implies the permissions
    144      * expressed in the <i>permission</i> object. Returns true if the
    145      * combination of permissions in the appropriate PermissionCollection
    146      * (e.g., a FilePermissionCollection for a FilePermission) together
    147      * imply the specified permission.
    148      *
    149      * <p>For example, suppose there is a FilePermissionCollection in this
    150      * Permissions object, and it contains one FilePermission that specifies
    151      * "read" access for  all files in all subdirectories of the "/tmp"
    152      * directory, and another FilePermission that specifies "write" access
    153      * for all files in the "/tmp/scratch/foo" directory.
    154      * Then if the {@code implies} method
    155      * is called with a permission specifying both "read" and "write" access
    156      * to files in the "/tmp/scratch/foo" directory, {@code true} is
    157      * returned.
    158      *
    159      * <p>Additionally, if this PermissionCollection contains the
    160      * AllPermission, this method will always return true.
    161      *
    162      * @param permission the Permission object to check.
    163      *
    164      * @return true if "permission" is implied by the permissions in the
    165      * PermissionCollection it
    166      * belongs to, false if not.
    167      */
    168     @Override
    169     public boolean implies(Permission permission) {
    170         // No sync; staleness -> skip optimization, which is OK
    171         if (allPermission != null) {
    172             return true; // AllPermission has already been added
    173         } else {
    174             PermissionCollection pc = getPermissionCollection(permission,
    175                 false);
    176             if (pc != null) {
    177                 return pc.implies(permission);
    178             } else {
    179                 // none found
    180                 return false;
    181             }
    182         }
    183     }
    184
    185     /**
    186      * Returns an enumeration of all the Permission objects in all the
    187      * PermissionCollections in this Permissions object.
    188      *
    189      * @return an enumeration of all the Permissions.
    190      */
    191     @Override
    192     public Enumeration<Permission> elements() {
    193         // go through each Permissions in the hash table
    194         // and call their elements() function.
    195
    196         return new PermissionsEnumerator(permsMap.values().iterator());
    197     }
    198
    199     /**
    200      * Gets the PermissionCollection in this Permissions object for
    201      * permissions whose type is the same as that of <i>p</i>.
    202      * For example, if <i>p</i> is a FilePermission,
    203      * the FilePermissionCollection
    204      * stored in this Permissions object will be returned.
    205      *
    206      * If createEmpty is true,
    207      * this method creates a new PermissionCollection object for the specified
    208      * type of permission objects if one does not yet exist.
    209      * To do so, it first calls the {@code newPermissionCollection} method
    210      * on <i>p</i>.  Subclasses of class Permission
    211      * override that method if they need to store their permissions in a
    212      * particular PermissionCollection object in order to provide the
    213      * correct semantics when the {@code PermissionCollection.implies}
    214      * method is called.
    215      * If the call returns a PermissionCollection, that collection is stored
    216      * in this Permissions object. If the call returns null and createEmpty
    217      * is true, then
    218      * this method instantiates and stores a default PermissionCollection
    219      * that uses a hashtable to store its permission objects.
    220      *
    221      * createEmpty is ignored when creating empty PermissionCollection
    222      * for unresolved permissions because of the overhead of determining the
    223      * PermissionCollection to use.
    224      *
    225      * createEmpty should be set to false when this method is invoked from
    226      * implies() because it incurs the additional overhead of creating and
    227      * adding an empty PermissionCollection that will just return false.
    228      * It should be set to true when invoked from add().
    229      */
    230     private PermissionCollection getPermissionCollection(Permission p,
    231                                                          boolean createEmpty) {
    232         PermissionCollection pc = permsMap.get(p.getClass());
    233         if ((!hasUnresolved && !createEmpty) || pc != null) {
    234             // Collection not to be created, or already created
    235             return pc;
    236         }
    237         return createPermissionCollection(p, createEmpty);
    238     }
    239
    240     private PermissionCollection createPermissionCollection(Permission p,
    241                                                             boolean createEmpty) {
    242         synchronized (permsMap) {
    243             // Re-read under lock
    244             Class<?> c = p.getClass();
    245             PermissionCollection pc = permsMap.get(c);
    246
    247             // Collection already created
    248             if (pc != null) {
    249                 return pc;
    250             }
    251
    252             // Create and add permission collection to map if it is absent.
    253             // Check for unresolved permissions
    254             pc = (hasUnresolved ? getUnresolvedPermissions(p) : null);
    255
    256             // if still null, create a new collection
    257             if (pc == null && createEmpty) {
    258
    259                 pc = p.newPermissionCollection();
    260
    261                 // still no PermissionCollection?
    262                 // We'll give them a PermissionsHash.
    263                 if (pc == null) {
    264                     pc = new PermissionsHash();
    265                 }
    266             }
    267             if (pc != null) {
    268                 // Add pc, resolving any race
    269                 PermissionCollection oldPc = permsMap.putIfAbsent(c, pc);
    270                 if (oldPc != null) {
    271                     pc = oldPc;
    272                 }
    273             }
    274             return pc;
    275         }
    276     }
    277
    278     /**
    279      * Resolves any unresolved permissions of type p.
    280      *
    281      * @param p the type of unresolved permission to resolve
    282      *
    283      * @return PermissionCollection containing the unresolved permissions,
    284      *  or null if there were no unresolved permissions of type p.
    285      *
    286      */
    287     private PermissionCollection getUnresolvedPermissions(Permission p)
    288     {
    289         UnresolvedPermissionCollection uc =
    290         (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class);
    291
    292         // we have no unresolved permissions if uc is null
    293         if (uc == null)
    294             return null;
    295
    296         List<UnresolvedPermission> unresolvedPerms =
    297                                         uc.getUnresolvedPermissions(p);
    298
    299         // we have no unresolved permissions of this type if unresolvedPerms is null
    300         if (unresolvedPerms == null)
    301             return null;
    302
    303         java.security.cert.Certificate[] certs = null;
    304
    305         Object[] signers = p.getClass().getSigners();
    306
    307         int n = 0;
    308         if (signers != null) {
    309             for (int j=0; j < signers.length; j++) {
    310                 if (signers[j] instanceof java.security.cert.Certificate) {
    311                     n++;
    312                 }
    313             }
    314             certs = new java.security.cert.Certificate[n];
    315             n = 0;
    316             for (int j=0; j < signers.length; j++) {
    317                 if (signers[j] instanceof java.security.cert.Certificate) {
    318                     certs[n++] = (java.security.cert.Certificate)signers[j];
    319                 }
    320             }
    321         }
    322
    323         PermissionCollection pc = null;
    324         synchronized (unresolvedPerms) {
    325             int len = unresolvedPerms.size();
    326             for (int i = 0; i < len; i++) {
    327                 UnresolvedPermission up = unresolvedPerms.get(i);
    328                 Permission perm = up.resolve(p, certs);
    329                 if (perm != null) {
    330                     if (pc == null) {
    331                         pc = p.newPermissionCollection();
    332                         if (pc == null)
    333                             pc = new PermissionsHash();
    334                     }
    335                     pc.add(perm);
    336                 }
    337             }
    338         }
    339         return pc;
    340     }
    341
    342     @java.io.Serial
    343     private static final long serialVersionUID = 4858622370623524688L;
    344
    345     // Need to maintain serialization interoperability with earlier releases,
    346     // which had the serializable field:
    347     // private Hashtable perms;
    348
    349     /**
    350      * @serialField perms java.util.Hashtable
    351      *     A table of the Permission classes and PermissionCollections.
    352      * @serialField allPermission java.security.PermissionCollection
    353      */
    354     @java.io.Serial
    355     private static final ObjectStreamField[] serialPersistentFields = {
    356         new ObjectStreamField("perms", Hashtable.class),
    357         new ObjectStreamField("allPermission", PermissionCollection.class),
    358     };
    359
    360     /**
    361      * @serialData Default fields.
    362      */
    363     /*
    364      * Writes the contents of the permsMap field out as a Hashtable for
    365      * serialization compatibility with earlier releases. allPermission
    366      * unchanged.
    367      */
    368     @java.io.Serial
    369     private void writeObject(ObjectOutputStream out) throws IOException {
    370         // Don't call out.defaultWriteObject()
    371
    372         // Copy perms into a Hashtable
    373         Hashtable<Class<?>, PermissionCollection> perms =
    374             new Hashtable<>(permsMap.size()*2); // no sync; estimate
    375         perms.putAll(permsMap);
    376
    377         // Write out serializable fields
    378         ObjectOutputStream.PutField pfields = out.putFields();
    379
    380         pfields.put("allPermission", allPermission); // no sync; staleness OK
    381         pfields.put("perms", perms);
    382         out.writeFields();
    383     }
    384
    385     /*
    386      * Reads in a Hashtable of Class/PermissionCollections and saves them in the
    387      * permsMap field. Reads in allPermission.
    388      */
    389     @java.io.Serial
    390     private void readObject(ObjectInputStream in) throws IOException,
    391     ClassNotFoundException {
    392         // Don't call defaultReadObject()
    393
    394         // Read in serialized fields
    395         ObjectInputStream.GetField gfields = in.readFields();
    396
    397         // Get allPermission
    398         allPermission = (PermissionCollection) gfields.get("allPermission", null);
    399
    400         // Get permissions
    401         // writeObject writes a Hashtable<Class<?>, PermissionCollection> for
    402         // the perms key, so this cast is safe, unless the data is corrupt.
    403         @SuppressWarnings("unchecked")
    404         Hashtable<Class<?>, PermissionCollection> perms =
    405             (Hashtable<Class<?>, PermissionCollection>)gfields.get("perms", null);
    406         permsMap = new ConcurrentHashMap<>(perms.size()*2);
    407         permsMap.putAll(perms);
    408
    409         // Check that Class is mapped to PermissionCollection containing
    410         // Permissions of the same class
    411         for (Map.Entry<Class<?>, PermissionCollection> e : perms.entrySet()) {
    412             Class<?> k = e.getKey();
    413             PermissionCollection v = e.getValue();
    414             Enumeration<Permission> en = v.elements();
    415             while (en.hasMoreElements()) {
    416                 Permission p = en.nextElement();
    417                 if (!k.equals(p.getClass())) {
    418                     throw new InvalidObjectException("Permission with class " +
    419                         k + " incorrectly mapped to PermissionCollection " +
    420                         "containing Permission with " + p.getClass());
    421                 }
    422             }
    423         }
    424
    425         // Set hasUnresolved
    426         UnresolvedPermissionCollection uc =
    427         (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class);
    428         hasUnresolved = (uc != null && uc.elements().hasMoreElements());
    429     }
    430 }
    431
    432 final class PermissionsEnumerator implements Enumeration<Permission> {
    433
    434     // all the perms
    435     private Iterator<PermissionCollection> perms;
    436     // the current set
    437     private Enumeration<Permission> permset;
    438
    439     PermissionsEnumerator(Iterator<PermissionCollection> e) {
    440         perms = e;
    441         permset = getNextEnumWithMore();
    442     }
    443
    444     // No need to synchronize; caller should sync on object as required
    445     public boolean hasMoreElements() {
    446         // if we enter with permissionimpl null, we know
    447         // there are no more left.
    448
    449         if (permset == null)
    450             return  false;
    451
    452         // try to see if there are any left in the current one
    453
    454         if (permset.hasMoreElements())
    455             return true;
    456
    457         // get the next one that has something in it...
    458         permset = getNextEnumWithMore();
    459
    460         // if it is null, we are done!
    461         return (permset != null);
    462     }
    463
    464     // No need to synchronize; caller should sync on object as required
    465     public Permission nextElement() {
    466
    467         // hasMoreElements will update permset to the next permset
    468         // with something in it...
    469
    470         if (hasMoreElements()) {
    471             return permset.nextElement();
    472         } else {
    473             throw new NoSuchElementException("PermissionsEnumerator");
    474         }
    475
    476     }
    477
    478     private Enumeration<Permission> getNextEnumWithMore() {
    479         while (perms.hasNext()) {
    480             PermissionCollection pc = perms.next();
    481             Enumeration<Permission> next =pc.elements();
    482             if (next.hasMoreElements())
    483                 return next;
    484         }
    485         return null;
    486
    487     }
    488 }
    489
    490 /**
    491  * A PermissionsHash stores a homogeneous set of permissions in a hashtable.
    492  *
    493  * @see Permission
    494  * @see Permissions
    495  *
    496  *
    497  * @author Roland Schemers
    498  *
    499  * @serial include
    500  */
    501
    502 final class PermissionsHash extends PermissionCollection
    503 implements Serializable
    504 {
    505     /**
    506      * Key and value are (same) permissions objects.
    507      * Not serialized; see serialization section at end of class.
    508      */
    509     private transient ConcurrentHashMap<Permission, Permission> permsMap;
    510
    511     /**
    512      * Create an empty PermissionsHash object.
    513      */
    514     PermissionsHash() {
    515         permsMap = new ConcurrentHashMap<>(11);
    516     }
    517
    518     /**
    519      * Adds a permission to the PermissionsHash.
    520      *
    521      * @param permission the Permission object to add.
    522      */
    523     @Override
    524     public void add(Permission permission) {
    525         permsMap.put(permission, permission);
    526     }
    527
    528     /**
    529      * Check and see if this set of permissions implies the permissions
    530      * expressed in "permission".
    531      *
    532      * @param permission the Permission object to compare
    533      *
    534      * @return true if "permission" is a proper subset of a permission in
    535      * the set, false if not.
    536      */
    537     @Override
    538     public boolean implies(Permission permission) {
    539         // attempt a fast lookup and implies. If that fails
    540         // then enumerate through all the permissions.
    541         Permission p = permsMap.get(permission);
    542
    543         // If permission is found, then p.equals(permission)
    544         if (p == null) {
    545             for (Permission p_ : permsMap.values()) {
    546                 if (p_.implies(permission))
    547                     return true;
    548             }
    549             return false;
    550         } else {
    551             return true;
    552         }
    553     }
    554
    555     /**
    556      * Returns an enumeration of all the Permission objects in the container.
    557      *
    558      * @return an enumeration of all the Permissions.
    559      */
    560     @Override
    561     public Enumeration<Permission> elements() {
    562         return permsMap.elements();
    563     }
    564
    565     @java.io.Serial
    566     private static final long serialVersionUID = -8491988220802933440L;
    567     // Need to maintain serialization interoperability with earlier releases,
    568     // which had the serializable field:
    569     // private Hashtable perms;
    570     /**
    571      * @serialField perms java.util.Hashtable
    572      *     A table of the Permissions (both key and value are same).
    573      */
    574     @java.io.Serial
    575     private static final ObjectStreamField[] serialPersistentFields = {
    576         new ObjectStreamField("perms", Hashtable.class),
    577     };
    578
    579     /*
    580      * @serialData Default fields.
    581      */
    582
    583     /**
    584      * Writes the contents of the permsMap field out as a Hashtable for
    585      * serialization compatibility with earlier releases.
    586      *
    587      * @param  out the {@code ObjectOutputStream} to which data is written
    588      * @throws IOException if an I/O error occurs
    589      */
    590     @java.io.Serial
    591     private void writeObject(ObjectOutputStream out) throws IOException {
    592         // Don't call out.defaultWriteObject()
    593
    594         // Copy perms into a Hashtable
    595         Hashtable<Permission, Permission> perms =
    596                 new Hashtable<>(permsMap.size()*2);
    597         perms.putAll(permsMap);
    598
    599         // Write out serializable fields
    600         ObjectOutputStream.PutField pfields = out.putFields();
    601         pfields.put("perms", perms);
    602         out.writeFields();
    603     }
    604
    605     /**
    606      * Reads in a Hashtable of Permission/Permission and saves them in the
    607      * permsMap field.
    608      *
    609      * @param  in the {@code ObjectInputStream} from which data is read
    610      * @throws IOException if an I/O error occurs
    611      * @throws ClassNotFoundException if a serialized class cannot be loaded
    612      */
    613     @java.io.Serial
    614     private void readObject(ObjectInputStream in) throws IOException,
    615     ClassNotFoundException {
    616         // Don't call defaultReadObject()
    617
    618         // Read in serialized fields
    619         ObjectInputStream.GetField gfields = in.readFields();
    620
    621         // Get permissions
    622         // writeObject writes a Hashtable<Class<?>, PermissionCollection> for
    623         // the perms key, so this cast is safe, unless the data is corrupt.
    624         @SuppressWarnings("unchecked")
    625         Hashtable<Permission, Permission> perms =
    626                 (Hashtable<Permission, Permission>)gfields.get("perms", null);
    627         permsMap = new ConcurrentHashMap<>(perms.size()*2);
    628         permsMap.putAll(perms);
    629
    630         // check that the Permission key and value are the same object
    631         for (Map.Entry<Permission, Permission> e : perms.entrySet()) {
    632             Permission k = e.getKey();
    633             Permission v = e.getValue();
    634             if (k != v) {
    635                 throw new InvalidObjectException("Permission (" + k +
    636                     ") incorrectly mapped to Permission (" + v + ")");
    637             }
    638         }
    639     }
    640 }
    --- EOF ---