      1 /*
      2  * Copyright (c) 1997, 2020, 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.IOException;
     29 import java.io.ObjectInputStream;
     30 import java.io.ObjectOutputStream;
     31 import java.io.ObjectStreamField;
     32 import java.util.Enumeration;
     33 import java.util.Hashtable;
     34 import java.util.concurrent.ConcurrentHashMap;
     35
     36 /**
     37  * The BasicPermission class extends the Permission class, and
     38  * can be used as the base class for permissions that want to
     39  * follow the same naming convention as BasicPermission.
     40  * <P>
     41  * The name for a BasicPermission is the name of the given permission
     42  * (for example, "exit",
     43  * "setFactory", "print.queueJob", etc). The naming
     44  * convention follows the  hierarchical property naming convention.
     45  * An asterisk may appear by itself, or if immediately preceded by a "."
     46  * may appear at the end of the name, to signify a wildcard match.
     47  * For example, "*" and "java.*" signify a wildcard match, while "*java", "a*b",
     48  * and "java*" do not.
     49  * <P>
     50  * The action string (inherited from Permission) is unused.
     51  * Thus, BasicPermission is commonly used as the base class for
     52  * "named" permissions
     53  * (ones that contain a name but no actions list; you either have the
     54  * named permission or you don't.)
     55  * Subclasses may implement actions on top of BasicPermission,
     56  * if desired.
     57  *
     58  * @see java.security.Permission
     59  * @see java.security.Permissions
     60  * @see java.security.PermissionCollection
     61  * @see java.lang.SecurityManager
     62  *
     63  * @author Marianne Mueller
     64  * @author Roland Schemers
     65  * @since 1.2
     66  */
     67
     68 public abstract class BasicPermission extends Permission
     69     implements java.io.Serializable
     70 {
     71
     72     @java.io.Serial
     73     private static final long serialVersionUID = 6279438298436773498L;
     74
     75     // does this permission have a wildcard at the end?
     76     private transient boolean wildcard;
     77
     78     // the name without the wildcard on the end
     79     private transient String path;
     80
     81     // is this permission the old-style exitVM permission (pre JDK 1.6)?
     82     private transient boolean exitVM;
     83
     84     /**
     85      * initialize a BasicPermission object. Common to all constructors.
     86      */
     87     private void init(String name) {
     88         if (name == null)
     89             throw new NullPointerException("name can't be null");
     90
     91         int len = name.length();
     92
     93         if (len == 0) {
     94             throw new IllegalArgumentException("name can't be empty");
     95         }
     96
     97         char last = name.charAt(len - 1);
     98
     99         // Is wildcard or ends with ".*"?
    100         if (last == '*' && (len == 1 || name.charAt(len - 2) == '.')) {
    101             wildcard = true;
    102             if (len == 1) {
    103                 path = "";
    104             } else {
    105                 path = name.substring(0, len - 1);
    106             }
    107         } else {
    108             if (name.equals("exitVM")) {
    109                 wildcard = true;
    110                 path = "exitVM.";
    111                 exitVM = true;
    112             } else {
    113                 path = name;
    114             }
    115         }
    116     }
    117
    118     /**
    119      * Creates a new BasicPermission with the specified name.
    120      * Name is the symbolic name of the permission, such as
    121      * "setFactory",
    122      * "print.queueJob", or "topLevelWindow", etc.
    123      *
    124      * @param name the name of the BasicPermission.
    125      *
    126      * @throws NullPointerException if {@code name} is {@code null}.
    127      * @throws IllegalArgumentException if {@code name} is empty.
    128      */
    129     public BasicPermission(String name) {
    130         super(name);
    131         init(name);
    132     }
    133
    134
    135     /**
    136      * Creates a new BasicPermission object with the specified name.
    137      * The name is the symbolic name of the BasicPermission, and the
    138      * actions String is currently unused.
    139      *
    140      * @param name the name of the BasicPermission.
    141      * @param actions ignored.
    142      *
    143      * @throws NullPointerException if {@code name} is {@code null}.
    144      * @throws IllegalArgumentException if {@code name} is empty.
    145      */
    146     public BasicPermission(String name, String actions) {
    147         super(name);
    148         init(name);
    149     }
    150
    151     /**
    152      * Checks if the specified permission is "implied" by
    153      * this object.
    154      * <P>
    155      * More specifically, this method returns true if:
    156      * <ul>
    157      * <li> {@code p}'s class is the same as this object's class, and
    158      * <li> {@code p}'s name equals or (in the case of wildcards)
    159      *      is implied by this object's
    160      *      name. For example, "a.b.*" implies "a.b.c".
    161      * </ul>
    162      *
    163      * @param p the permission to check against.
    164      *
    165      * @return true if the passed permission is equal to or
    166      * implied by this permission, false otherwise.
    167      */
    168     @Override
    169     public boolean implies(Permission p) {
    170         if ((p == null) || (p.getClass() != getClass()))
    171             return false;
    172
    173         BasicPermission that = (BasicPermission) p;
    174
    175         if (this.wildcard) {
    176             if (that.wildcard) {
    177                 // one wildcard can imply another
    178                 return that.path.startsWith(path);
    179             } else {
    180                 // make sure ap.path is longer so a.b.* doesn't imply a.b
    181                 return (that.path.length() > this.path.length()) &&
    182                     that.path.startsWith(this.path);
    183             }
    184         } else {
    185             if (that.wildcard) {
    186                 // a non-wildcard can't imply a wildcard
    187                 return false;
    188             }
    189             else {
    190                 return this.path.equals(that.path);
    191             }
    192         }
    193     }
    194
    195     /**
    196      * Checks two BasicPermission objects for equality.
    197      * Checks that {@code obj}'s class is the same as this object's class
    198      * and has the same name as this object.
    199      *
    200      * @param obj the object we are testing for equality with this object.
    201      * @return true if {@code obj}'s class is the same as this object's class
    202      *  and has the same name as this BasicPermission object, false otherwise.
    203      */
    204     @Override
    205     public boolean equals(Object obj) {
    206         if (obj == this)
    207             return true;
    208
    209         if ((obj == null) || (obj.getClass() != getClass()))
    210             return false;
    211
    212         BasicPermission bp = (BasicPermission) obj;
    213
    214         return getName().equals(bp.getName());
    215     }
    216
    217
    218     /**
    219      * Returns the hash code value for this object.
    220      * The hash code used is the hash code of the name, that is,
    221      * {@code getName().hashCode()}, where {@code getName} is
    222      * from the Permission superclass.
    223      *
    224      * @return a hash code value for this object.
    225      */
    226     @Override
    227     public int hashCode() {
    228         return this.getName().hashCode();
    229     }
    230
    231     /**
    232      * Returns the canonical string representation of the actions,
    233      * which currently is the empty string "", since there are no actions for
    234      * a BasicPermission.
    235      *
    236      * @return the empty string "".
    237      */
    238     @Override
    239     public String getActions() {
    240         return "";
    241     }
    242
    243     /**
    244      * Returns a new PermissionCollection object for storing BasicPermission
    245      * objects.
    246      *
    247      * <p>BasicPermission objects must be stored in a manner that allows them
    248      * to be inserted in any order, but that also enables the
    249      * PermissionCollection {@code implies} method
    250      * to be implemented in an efficient (and consistent) manner.
    251      *
    252      * @return a new PermissionCollection object suitable for
    253      * storing BasicPermissions.
    254      */
    255     @Override
    256     public PermissionCollection newPermissionCollection() {
    257         return new BasicPermissionCollection(this.getClass());
    258     }
    259
    260     /**
    261      * readObject is called to restore the state of the BasicPermission from
    262      * a stream.

    263      */
    264     @java.io.Serial
    265     private void readObject(ObjectInputStream s)
    266          throws IOException, ClassNotFoundException
    267     {
    268         s.defaultReadObject();
    269         // init is called to initialize the rest of the values.
    270         init(getName());
    271     }
    272
    273     /**
    274      * Returns the canonical name of this BasicPermission.
    275      * All internal invocations of getName should invoke this method, so
    276      * that the pre-JDK 1.6 "exitVM" and current "exitVM.*" permission are
    277      * equivalent in equals/hashCode methods.
    278      *
    279      * @return the canonical name of this BasicPermission.
    280      */
    281     final String getCanonicalName() {
    282         return exitVM ? "exitVM.*" : getName();
    283     }
    284 }
    285
    286 /**
    287  * A BasicPermissionCollection stores a collection
    288  * of BasicPermission permissions. BasicPermission objects
    289  * must be stored in a manner that allows them to be inserted in any
    290  * order, but enable the implies function to evaluate the implies
    291  * method in an efficient (and consistent) manner.
    292  *
    293  * A BasicPermissionCollection handles comparing a permission like "a.b.c.d.e"
    294  * with a Permission such as "a.b.*", or "*".
    295  *
    296  * @see java.security.Permission
    297  * @see java.security.Permissions
    298  *
    299  *
    300  * @author Roland Schemers
    301  *
    302  * @serial include
    303  */
    304
    305 final class BasicPermissionCollection
    306     extends PermissionCollection
    307     implements java.io.Serializable
    308 {
    309
    310     @java.io.Serial
    311     private static final long serialVersionUID = 739301742472979399L;
    312
    313     /**
    314      * Key is name, value is permission. All permission objects in
    315      * collection must be of the same type.
    316      * Not serialized; see serialization section at end of class.
    317      */
    318     private transient ConcurrentHashMap<String, Permission> perms;
    319
    320     /**
    321      * This is set to {@code true} if this BasicPermissionCollection
    322      * contains a BasicPermission with '*' as its permission name.
    323      *
    324      * @see #serialPersistentFields
    325      */
    326     private boolean all_allowed;
    327
    328     /**
    329      * The class to which all BasicPermissions in this
    330      * BasicPermissionCollection belong.
    331      *
    332      * @see #serialPersistentFields
    333      */
    334     private Class<?> permClass;
    335
    336     /**
    337      * Create an empty BasicPermissionCollection object.
    338      *
    339      */
    340     public BasicPermissionCollection(Class<?> clazz) {
    341         perms = new ConcurrentHashMap<>(11);
    342         all_allowed = false;
    343         permClass = clazz;
    344     }
    345
    346     /**
    347      * Adds a permission to the BasicPermissions. The key for the hash is
    348      * permission.path.
    349      *
    350      * @param permission the Permission object to add.
    351      *
    352      * @throws    IllegalArgumentException   if the permission is not a
    353      *                                       BasicPermission, or if
    354      *                                       the permission is not of the
    355      *                                       same Class as the other
    356      *                                       permissions in this collection.
    357      *
    358      * @throws    SecurityException   if this BasicPermissionCollection object
    359      *                                has been marked readonly
    360      */
    361     @Override
    362     public void add(Permission permission) {
    363         if (! (permission instanceof BasicPermission))
    364             throw new IllegalArgumentException("invalid permission: "+
    365                                                permission);
    366         if (isReadOnly())
    367             throw new SecurityException("attempt to add a Permission to a readonly PermissionCollection");
    368
    369         BasicPermission bp = (BasicPermission) permission;
    370
    371         // make sure we only add new BasicPermissions of the same class
    372         // Also check null for compatibility with deserialized form from
    373         // previous versions.
    374         if (permClass == null) {
    375             // adding first permission
    376             permClass = bp.getClass();
    377         } else {
    378             if (bp.getClass() != permClass)
    379                 throw new IllegalArgumentException("invalid permission: " +
    380                                                 permission);
    381         }
    382
    383         String canonName = bp.getCanonicalName();
    384         perms.put(canonName, permission);
    385
    386         // No sync on all_allowed; staleness OK
    387         if (!all_allowed) {
    388             if (canonName.equals("*"))
    389                 all_allowed = true;
    390         }
    391     }
    392
    393     /**
    394      * Check and see if this set of permissions implies the permissions
    395      * expressed in "permission".
    396      *
    397      * @param permission the Permission object to compare
    398      *
    399      * @return true if "permission" is a proper subset of a permission in
    400      * the set, false if not.
    401      */
    402     @Override
    403     public boolean implies(Permission permission) {
    404         if (! (permission instanceof BasicPermission))
    405             return false;
    406
    407         BasicPermission bp = (BasicPermission) permission;
    408
    409         // random subclasses of BasicPermission do not imply each other
    410         if (bp.getClass() != permClass)
    411             return false;
    412
    413         // short circuit if the "*" Permission was added
    414         if (all_allowed)
    415             return true;
    416
    417         // strategy:
    418         // Check for full match first. Then work our way up the
    419         // path looking for matches on a.b..*
    420
    421         String path = bp.getCanonicalName();
    422         //System.out.println("check "+path);
    423
    424         Permission x = perms.get(path);
    425
    426         if (x != null) {
    427             // we have a direct hit!
    428             return x.implies(permission);
    429         }
    430
    431         // work our way up the tree...
    432         int last, offset;
    433
    434         offset = path.length()-1;
    435
    436         while ((last = path.lastIndexOf('.', offset)) != -1) {
    437
    438             path = path.substring(0, last+1) + "*";
    439             //System.out.println("check "+path);
    440
    441             x = perms.get(path);
    442
    443             if (x != null) {
    444                 return x.implies(permission);
    445             }
    446             offset = last -1;
    447         }
    448
    449         // we don't have to check for "*" as it was already checked
    450         // at the top (all_allowed), so we just return false
    451         return false;
    452     }
    453
    454     /**
    455      * Returns an enumeration of all the BasicPermission objects in the
    456      * container.
    457      *
    458      * @return an enumeration of all the BasicPermission objects.
    459      */
    460     @Override
    461     public Enumeration<Permission> elements() {
    462         return perms.elements();
    463     }
    464
    465     // Need to maintain serialization interoperability with earlier releases,
    466     // which had the serializable field:
    467     //
    468     // @serial the Hashtable is indexed by the BasicPermission name
    469     //
    470     // private Hashtable permissions;
    471     /**
    472      * @serialField permissions java.util.Hashtable
    473      *    The BasicPermissions in this BasicPermissionCollection.
    474      *    All BasicPermissions in the collection must belong to the same class.
    475      *    The Hashtable is indexed by the BasicPermission name; the value
    476      *    of the Hashtable entry is the permission.
    477      * @serialField all_allowed boolean
    478      *   This is set to {@code true} if this BasicPermissionCollection
    479      *   contains a BasicPermission with '*' as its permission name.
    480      * @serialField permClass java.lang.Class
    481      *   The class to which all BasicPermissions in this
    482      *   BasicPermissionCollection belongs.
    483      */
    484     @java.io.Serial
    485     private static final ObjectStreamField[] serialPersistentFields = {
    486         new ObjectStreamField("permissions", Hashtable.class),
    487         new ObjectStreamField("all_allowed", Boolean.TYPE),
    488         new ObjectStreamField("permClass", Class.class),
    489     };
    490
    491     /**
    492      * @serialData Default fields.
    493      */
    494     /*

    495      * Writes the contents of the perms field out as a Hashtable for
    496      * serialization compatibility with earlier releases. all_allowed
    497      * and permClass unchanged.

    498      */
    499     @java.io.Serial
    500     private void writeObject(ObjectOutputStream out) throws IOException {
    501         // Don't call out.defaultWriteObject()
    502
    503         // Copy perms into a Hashtable
    504         Hashtable<String, Permission> permissions =
    505                 new Hashtable<>(perms.size()*2);
    506
    507         permissions.putAll(perms);
    508
    509         // Write out serializable fields
    510         ObjectOutputStream.PutField pfields = out.putFields();
    511         pfields.put("all_allowed", all_allowed);
    512         pfields.put("permissions", permissions);
    513         pfields.put("permClass", permClass);
    514         out.writeFields();
    515     }
    516
    517     /**
    518      * readObject is called to restore the state of the
    519      * BasicPermissionCollection from a stream.

    520      */
    521     @java.io.Serial
    522     private void readObject(java.io.ObjectInputStream in)
    523          throws IOException, ClassNotFoundException
    524     {
    525         // Don't call defaultReadObject()
    526
    527         // Read in serialized fields
    528         ObjectInputStream.GetField gfields = in.readFields();
    529
    530         // Get permissions
    531         // writeObject writes a Hashtable<String, Permission> for the
    532         // permissions key, so this cast is safe, unless the data is corrupt.
    533         @SuppressWarnings("unchecked")
    534         Hashtable<String, Permission> permissions =
    535                 (Hashtable<String, Permission>)gfields.get("permissions", null);
    536         perms = new ConcurrentHashMap<>(permissions.size()*2);
    537         perms.putAll(permissions);
    538
    539         // Get all_allowed
    540         all_allowed = gfields.get("all_allowed", false);
    541
    542         // Get permClass
    543         permClass = (Class<?>) gfields.get("permClass", null);
    544
    545         if (permClass == null) {
    546             // set permClass
    547             Enumeration<Permission> e = permissions.elements();
    548             if (e.hasMoreElements()) {
    549                 Permission p = e.nextElement();
    550                 permClass = p.getClass();
    551             }
    552         }
    553     }
    554 }
    --- EOF ---