1 /*
   2  * Copyright (c) 1997, 2019, 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      * @exception 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      * @exception 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 }