1 /*
   2  * Copyright (c) 1997, 2015, 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.util.Enumeration;
  29 import java.util.Map;
  30 import java.util.HashMap;
  31 import java.util.Hashtable;
  32 import java.util.Collections;
  33 import java.io.ObjectStreamField;
  34 import java.io.ObjectOutputStream;
  35 import java.io.ObjectInputStream;
  36 import java.io.IOException;
  37 
  38 /**
  39  * The BasicPermission class extends the Permission class, and
  40  * can be used as the base class for permissions that want to
  41  * follow the same naming convention as BasicPermission.
  42  * <P>
  43  * The name for a BasicPermission is the name of the given permission
  44  * (for example, "exit",
  45  * "setFactory", "print.queueJob", etc). The naming
  46  * convention follows the  hierarchical property naming convention.
  47  * An asterisk may appear by itself, or if immediately preceded by a "."
  48  * may appear at the end of the name, to signify a wildcard match.
  49  * For example, "*" and "java.*" signify a wildcard match, while "*java", "a*b",
  50  * and "java*" do not.
  51  * <P>
  52  * The action string (inherited from Permission) is unused.
  53  * Thus, BasicPermission is commonly used as the base class for
  54  * "named" permissions
  55  * (ones that contain a name but no actions list; you either have the
  56  * named permission or you don't.)
  57  * Subclasses may implement actions on top of BasicPermission,
  58  * if desired.
  59  *
  60  * @see java.security.Permission
  61  * @see java.security.Permissions
  62  * @see java.security.PermissionCollection
  63  * @see java.lang.SecurityManager
  64  *
  65  * @author Marianne Mueller
  66  * @author Roland Schemers
  67  */
  68 
  69 public abstract class BasicPermission extends Permission
  70     implements java.io.Serializable
  71 {
  72 
  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     public boolean implies(Permission p) {
 169         if ((p == null) || (p.getClass() != getClass()))
 170             return false;
 171 
 172         BasicPermission that = (BasicPermission) p;
 173 
 174         if (this.wildcard) {
 175             if (that.wildcard) {
 176                 // one wildcard can imply another
 177                 return that.path.startsWith(path);
 178             } else {
 179                 // make sure ap.path is longer so a.b.* doesn't imply a.b
 180                 return (that.path.length() > this.path.length()) &&
 181                     that.path.startsWith(this.path);
 182             }
 183         } else {
 184             if (that.wildcard) {
 185                 // a non-wildcard can't imply a wildcard
 186                 return false;
 187             }
 188             else {
 189                 return this.path.equals(that.path);
 190             }
 191         }
 192     }
 193 
 194     /**
 195      * Checks two BasicPermission objects for equality.
 196      * Checks that {@code obj}'s class is the same as this object's class
 197      * and has the same name as this object.
 198      *
 199      * @param obj the object we are testing for equality with this object.
 200      * @return true if {@code obj}'s class is the same as this object's class
 201      *  and has the same name as this BasicPermission object, false otherwise.
 202      */
 203     public boolean equals(Object obj) {
 204         if (obj == this)
 205             return true;
 206 
 207         if ((obj == null) || (obj.getClass() != getClass()))
 208             return false;
 209 
 210         BasicPermission bp = (BasicPermission) obj;
 211 
 212         return getName().equals(bp.getName());
 213     }
 214 
 215 
 216     /**
 217      * Returns the hash code value for this object.
 218      * The hash code used is the hash code of the name, that is,
 219      * {@code getName().hashCode()}, where {@code getName} is
 220      * from the Permission superclass.
 221      *
 222      * @return a hash code value for this object.
 223      */
 224     public int hashCode() {
 225         return this.getName().hashCode();
 226     }
 227 
 228     /**
 229      * Returns the canonical string representation of the actions,
 230      * which currently is the empty string "", since there are no actions for
 231      * a BasicPermission.
 232      *
 233      * @return the empty string "".
 234      */
 235     public String getActions() {
 236         return "";
 237     }
 238 
 239     /**
 240      * Returns a new PermissionCollection object for storing BasicPermission
 241      * objects.
 242      *
 243      * <p>BasicPermission objects must be stored in a manner that allows them
 244      * to be inserted in any order, but that also enables the
 245      * PermissionCollection {@code implies} method
 246      * to be implemented in an efficient (and consistent) manner.
 247      *
 248      * @return a new PermissionCollection object suitable for
 249      * storing BasicPermissions.
 250      */
 251     public PermissionCollection newPermissionCollection() {
 252         return new BasicPermissionCollection(this.getClass());
 253     }
 254 
 255     /**
 256      * readObject is called to restore the state of the BasicPermission from
 257      * a stream.
 258      */
 259     private void readObject(ObjectInputStream s)
 260          throws IOException, ClassNotFoundException
 261     {
 262         s.defaultReadObject();
 263         // init is called to initialize the rest of the values.
 264         init(getName());
 265     }
 266 
 267     /**
 268      * Returns the canonical name of this BasicPermission.
 269      * All internal invocations of getName should invoke this method, so
 270      * that the pre-JDK 1.6 "exitVM" and current "exitVM.*" permission are
 271      * equivalent in equals/hashCode methods.
 272      *
 273      * @return the canonical name of this BasicPermission.
 274      */
 275     final String getCanonicalName() {
 276         return exitVM ? "exitVM.*" : getName();
 277     }
 278 }
 279 
 280 /**
 281  * A BasicPermissionCollection stores a collection
 282  * of BasicPermission permissions. BasicPermission objects
 283  * must be stored in a manner that allows them to be inserted in any
 284  * order, but enable the implies function to evaluate the implies
 285  * method in an efficient (and consistent) manner.
 286  *
 287  * A BasicPermissionCollection handles comparing a permission like "a.b.c.d.e"
 288  * with a Permission such as "a.b.*", or "*".
 289  *
 290  * @see java.security.Permission
 291  * @see java.security.Permissions
 292  *
 293  *
 294  * @author Roland Schemers
 295  *
 296  * @serial include
 297  */
 298 
 299 final class BasicPermissionCollection
 300     extends PermissionCollection
 301     implements java.io.Serializable
 302 {
 303 
 304     private static final long serialVersionUID = 739301742472979399L;
 305 
 306     /**
 307       * Key is name, value is permission. All permission objects in
 308       * collection must be of the same type.
 309       * Not serialized; see serialization section at end of class.
 310       */
 311     private transient Map<String, Permission> perms;
 312 
 313     /**
 314      * This is set to {@code true} if this BasicPermissionCollection
 315      * contains a BasicPermission with '*' as its permission name.
 316      *
 317      * @see #serialPersistentFields
 318      */
 319     private boolean all_allowed;
 320 
 321     /**
 322      * The class to which all BasicPermissions in this
 323      * BasicPermissionCollection belongs.
 324      *
 325      * @see #serialPersistentFields
 326      */
 327     private Class<?> permClass;
 328 
 329     /**
 330      * Create an empty BasicPermissionCollection object.
 331      *
 332      */
 333 
 334     public BasicPermissionCollection(Class<?> clazz) {
 335         perms = new HashMap<String, Permission>(11);
 336         all_allowed = false;
 337         permClass = clazz;
 338     }
 339 
 340     /**
 341      * Adds a permission to the BasicPermissions. The key for the hash is
 342      * permission.path.
 343      *
 344      * @param permission the Permission object to add.
 345      *
 346      * @exception IllegalArgumentException - if the permission is not a
 347      *                                       BasicPermission, or if
 348      *                                       the permission is not of the
 349      *                                       same Class as the other
 350      *                                       permissions in this collection.
 351      *
 352      * @exception SecurityException - if this BasicPermissionCollection object
 353      *                                has been marked readonly
 354      */
 355     public void add(Permission permission) {
 356         if (! (permission instanceof BasicPermission))
 357             throw new IllegalArgumentException("invalid permission: "+
 358                                                permission);
 359         if (isReadOnly())
 360             throw new SecurityException("attempt to add a Permission to a readonly PermissionCollection");
 361 
 362         BasicPermission bp = (BasicPermission) permission;
 363 
 364         // make sure we only add new BasicPermissions of the same class
 365         // Also check null for compatibility with deserialized form from
 366         // previous versions.
 367         if (permClass == null) {
 368             // adding first permission
 369             permClass = bp.getClass();
 370         } else {
 371             if (bp.getClass() != permClass)
 372                 throw new IllegalArgumentException("invalid permission: " +
 373                                                 permission);
 374         }
 375 
 376         synchronized (this) {
 377             perms.put(bp.getCanonicalName(), permission);
 378         }
 379 
 380         // No sync on all_allowed; staleness OK
 381         if (!all_allowed) {
 382             if (bp.getCanonicalName().equals("*"))
 383                 all_allowed = true;
 384         }
 385     }
 386 
 387     /**
 388      * Check and see if this set of permissions implies the permissions
 389      * expressed in "permission".
 390      *
 391      * @param permission the Permission object to compare
 392      *
 393      * @return true if "permission" is a proper subset of a permission in
 394      * the set, false if not.
 395      */
 396     public boolean implies(Permission permission) {
 397         if (! (permission instanceof BasicPermission))
 398             return false;
 399 
 400         BasicPermission bp = (BasicPermission) permission;
 401 
 402         // random subclasses of BasicPermission do not imply each other
 403         if (bp.getClass() != permClass)
 404             return false;
 405 
 406         // short circuit if the "*" Permission was added
 407         if (all_allowed)
 408             return true;
 409 
 410         // strategy:
 411         // Check for full match first. Then work our way up the
 412         // path looking for matches on a.b..*
 413 
 414         String path = bp.getCanonicalName();
 415         //System.out.println("check "+path);
 416 
 417         Permission x;
 418 
 419         synchronized (this) {
 420             x = perms.get(path);
 421         }
 422 
 423         if (x != null) {
 424             // we have a direct hit!
 425             return x.implies(permission);
 426         }
 427 
 428         // work our way up the tree...
 429         int last, offset;
 430 
 431         offset = path.length()-1;
 432 
 433         while ((last = path.lastIndexOf('.', offset)) != -1) {
 434 
 435             path = path.substring(0, last+1) + "*";
 436             //System.out.println("check "+path);
 437 
 438             synchronized (this) {
 439                 x = perms.get(path);
 440             }
 441 
 442             if (x != null) {
 443                 return x.implies(permission);
 444             }
 445             offset = last -1;
 446         }
 447 
 448         // we don't have to check for "*" as it was already checked
 449         // at the top (all_allowed), so we just return false
 450         return false;
 451     }
 452 
 453     /**
 454      * Returns an enumeration of all the BasicPermission objects in the
 455      * container.
 456      *
 457      * @return an enumeration of all the BasicPermission objects.
 458      */
 459     public Enumeration<Permission> elements() {
 460         // Convert Iterator of Map values into an Enumeration
 461         synchronized (this) {
 462             return Collections.enumeration(perms.values());
 463         }
 464     }
 465 
 466     // Need to maintain serialization interoperability with earlier releases,
 467     // which had the serializable field:
 468     //
 469     // @serial the Hashtable is indexed by the BasicPermission name
 470     //
 471     // private Hashtable permissions;
 472     /**
 473      * @serialField permissions java.util.Hashtable
 474      *    The BasicPermissions in this BasicPermissionCollection.
 475      *    All BasicPermissions in the collection must belong to the same class.
 476      *    The Hashtable is indexed by the BasicPermission name; the value
 477      *    of the Hashtable entry is the permission.
 478      * @serialField all_allowed boolean
 479      *   This is set to {@code true} if this BasicPermissionCollection
 480      *   contains a BasicPermission with '*' as its permission name.
 481      * @serialField permClass java.lang.Class
 482      *   The class to which all BasicPermissions in this
 483      *   BasicPermissionCollection belongs.
 484      */
 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     private void writeObject(ObjectOutputStream out) throws IOException {
 500         // Don't call out.defaultWriteObject()
 501 
 502         // Copy perms into a Hashtable
 503         Hashtable<String, Permission> permissions =
 504                 new Hashtable<>(perms.size()*2);
 505 
 506         synchronized (this) {
 507             permissions.putAll(perms);
 508         }
 509 
 510         // Write out serializable fields
 511         ObjectOutputStream.PutField pfields = out.putFields();
 512         pfields.put("all_allowed", all_allowed);
 513         pfields.put("permissions", permissions);
 514         pfields.put("permClass", permClass);
 515         out.writeFields();
 516     }
 517 
 518     /**
 519      * readObject is called to restore the state of the
 520      * BasicPermissionCollection from a stream.
 521      */
 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 HashMap<String, Permission>(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 }