1 /*
   2  * Copyright (c) 1997, 2012, 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  * <p>
  60  * <P>
  61  * @see java.security.Permission
  62  * @see java.security.Permissions
  63  * @see java.security.PermissionCollection
  64  * @see java.lang.SecurityManager
  65  *
  66  * @author Marianne Mueller
  67  * @author Roland Schemers
  68  */
  69 
  70 public abstract class BasicPermission extends Permission
  71     implements java.io.Serializable
  72 {
  73 
  74     private static final long serialVersionUID = 6279438298436773498L;
  75 
  76     // does this permission have a wildcard at the end?
  77     private transient boolean wildcard;
  78 
  79     // the name without the wildcard on the end
  80     private transient String path;
  81 
  82     // is this permission the old-style exitVM permission (pre JDK 1.6)?
  83     private transient boolean exitVM;
  84 
  85     /**
  86      * initialize a BasicPermission object. Common to all constructors.
  87      */
  88     private void init(String name) {
  89         if (name == null)
  90             throw new NullPointerException("name can't be null");
  91 
  92         int len = name.length();
  93 
  94         if (len == 0) {
  95             throw new IllegalArgumentException("name can't be empty");
  96         }
  97 
  98         char last = name.charAt(len - 1);
  99 
 100         // Is wildcard or ends with ".*"?
 101         if (last == '*' && (len == 1 || name.charAt(len - 2) == '.')) {
 102             wildcard = true;
 103             if (len == 1) {
 104                 path = "";
 105             } else {
 106                 path = name.substring(0, len - 1);
 107             }
 108         } else {
 109             if (name.equals("exitVM")) {
 110                 wildcard = true;
 111                 path = "exitVM.";
 112                 exitVM = true;
 113             } else {
 114                 path = name;
 115             }
 116         }
 117     }
 118 
 119     /**
 120      * Creates a new BasicPermission with the specified name.
 121      * Name is the symbolic name of the permission, such as
 122      * "setFactory",
 123      * "print.queueJob", or "topLevelWindow", etc.
 124      *
 125      * @param name the name of the BasicPermission.
 126      *
 127      * @throws NullPointerException if <code>name</code> is <code>null</code>.
 128      * @throws IllegalArgumentException if <code>name</code> is empty.
 129      */
 130     public BasicPermission(String name) {
 131         super(name);
 132         init(name);
 133     }
 134 
 135 
 136     /**
 137      * Creates a new BasicPermission object with the specified name.
 138      * The name is the symbolic name of the BasicPermission, and the
 139      * actions String is currently unused.
 140      *
 141      * @param name the name of the BasicPermission.
 142      * @param actions ignored.
 143      *
 144      * @throws NullPointerException if <code>name</code> is <code>null</code>.
 145      * @throws IllegalArgumentException if <code>name</code> is empty.
 146      */
 147     public BasicPermission(String name, String actions) {
 148         super(name);
 149         init(name);
 150     }
 151 
 152     /**
 153      * Checks if the specified permission is "implied" by
 154      * this object.
 155      * <P>
 156      * More specifically, this method returns true if:<p>
 157      * <ul>
 158      * <li> <i>p</i>'s class is the same as this object's class, and<p>
 159      * <li> <i>p</i>'s name equals or (in the case of wildcards)
 160      *      is implied by this object's
 161      *      name. For example, "a.b.*" implies "a.b.c".
 162      * </ul>
 163      *
 164      * @param p the permission to check against.
 165      *
 166      * @return true if the passed permission is equal to or
 167      * implied by this permission, false otherwise.
 168      */
 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 <i>obj</i>'s class is the same as this object's class
 198      * and has the same name as this object.
 199      * <P>
 200      * @param obj the object we are testing for equality with this object.
 201      * @return true if <i>obj</i>'s class is the same as this object's class
 202      *  and has the same name as this BasicPermission object, false otherwise.
 203      */
 204     public boolean equals(Object obj) {
 205         if (obj == this)
 206             return true;
 207 
 208         if ((obj == null) || (obj.getClass() != getClass()))
 209             return false;
 210 
 211         BasicPermission bp = (BasicPermission) obj;
 212 
 213         return getName().equals(bp.getName());
 214     }
 215 
 216 
 217     /**
 218      * Returns the hash code value for this object.
 219      * The hash code used is the hash code of the name, that is,
 220      * <code>getName().hashCode()</code>, where <code>getName</code> is
 221      * from the Permission superclass.
 222      *
 223      * @return a hash code value for this object.
 224      */
 225     public int hashCode() {
 226         return this.getName().hashCode();
 227     }
 228 
 229     /**
 230      * Returns the canonical string representation of the actions,
 231      * which currently is the empty string "", since there are no actions for
 232      * a BasicPermission.
 233      *
 234      * @return the empty string "".
 235      */
 236     public String getActions() {
 237         return "";
 238     }
 239 
 240     /**
 241      * Returns a new PermissionCollection object for storing BasicPermission
 242      * objects.
 243      *
 244      * <p>BasicPermission objects must be stored in a manner that allows them
 245      * to be inserted in any order, but that also enables the
 246      * PermissionCollection <code>implies</code> method
 247      * to be implemented in an efficient (and consistent) manner.
 248      *
 249      * @return a new PermissionCollection object suitable for
 250      * storing BasicPermissions.
 251      */
 252     public PermissionCollection newPermissionCollection() {
 253         return new BasicPermissionCollection(this.getClass());
 254     }
 255 
 256     /**
 257      * readObject is called to restore the state of the BasicPermission from
 258      * a stream.
 259      */
 260     private void readObject(ObjectInputStream s)
 261          throws IOException, ClassNotFoundException
 262     {
 263         s.defaultReadObject();
 264         // init is called to initialize the rest of the values.
 265         init(getName());
 266     }
 267 
 268     /**
 269      * Returns the canonical name of this BasicPermission.
 270      * All internal invocations of getName should invoke this method, so
 271      * that the pre-JDK 1.6 "exitVM" and current "exitVM.*" permission are
 272      * equivalent in equals/hashCode methods.
 273      *
 274      * @return the canonical name of this BasicPermission.
 275      */
 276     final String getCanonicalName() {
 277         return exitVM ? "exitVM.*" : getName();
 278     }
 279 }
 280 
 281 /**
 282  * A BasicPermissionCollection stores a collection
 283  * of BasicPermission permissions. BasicPermission objects
 284  * must be stored in a manner that allows them to be inserted in any
 285  * order, but enable the implies function to evaluate the implies
 286  * method in an efficient (and consistent) manner.
 287  *
 288  * A BasicPermissionCollection handles comparing a permission like "a.b.c.d.e"
 289  * with a Permission such as "a.b.*", or "*".
 290  *
 291  * @see java.security.Permission
 292  * @see java.security.Permissions
 293  *
 294  *
 295  * @author Roland Schemers
 296  *
 297  * @serial include
 298  */
 299 
 300 final class BasicPermissionCollection
 301     extends PermissionCollection
 302     implements java.io.Serializable
 303 {
 304 
 305     private static final long serialVersionUID = 739301742472979399L;
 306 
 307     /**
 308       * Key is name, value is permission. All permission objects in
 309       * collection must be of the same type.
 310       * Not serialized; see serialization section at end of class.
 311       */
 312     private transient Map<String, Permission> perms;
 313 
 314     /**
 315      * This is set to <code>true</code> if this BasicPermissionCollection
 316      * contains a BasicPermission with '*' as its permission name.
 317      *
 318      * @see #serialPersistentFields
 319      */
 320     private boolean all_allowed;
 321 
 322     /**
 323      * The class to which all BasicPermissions in this
 324      * BasicPermissionCollection belongs.
 325      *
 326      * @see #serialPersistentFields
 327      */
 328     private Class<?> permClass;
 329 
 330     /**
 331      * Create an empty BasicPermissionCollection object.
 332      *
 333      */
 334 
 335     public BasicPermissionCollection(Class<?> clazz) {
 336         perms = new HashMap<String, Permission>(11);
 337         all_allowed = false;
 338         permClass = clazz;
 339     }
 340 
 341     /**
 342      * Adds a permission to the BasicPermissions. The key for the hash is
 343      * permission.path.
 344      *
 345      * @param permission the Permission object to add.
 346      *
 347      * @exception IllegalArgumentException - if the permission is not a
 348      *                                       BasicPermission, or if
 349      *                                       the permission is not of the
 350      *                                       same Class as the other
 351      *                                       permissions in this collection.
 352      *
 353      * @exception SecurityException - if this BasicPermissionCollection object
 354      *                                has been marked readonly
 355      */
 356     public void add(Permission permission) {
 357         if (! (permission instanceof BasicPermission))
 358             throw new IllegalArgumentException("invalid permission: "+
 359                                                permission);
 360         if (isReadOnly())
 361             throw new SecurityException("attempt to add a Permission to a readonly PermissionCollection");
 362 
 363         BasicPermission bp = (BasicPermission) permission;
 364 
 365         // make sure we only add new BasicPermissions of the same class
 366         // Also check null for compatibility with deserialized form from
 367         // previous versions.
 368         if (permClass == null) {
 369             // adding first permission
 370             permClass = bp.getClass();
 371         } else {
 372             if (bp.getClass() != permClass)
 373                 throw new IllegalArgumentException("invalid permission: " +
 374                                                 permission);
 375         }
 376 
 377         synchronized (this) {
 378             perms.put(bp.getCanonicalName(), permission);
 379         }
 380 
 381         // No sync on all_allowed; staleness OK
 382         if (!all_allowed) {
 383             if (bp.getCanonicalName().equals("*"))
 384                 all_allowed = true;
 385         }
 386     }
 387 
 388     /**
 389      * Check and see if this set of permissions implies the permissions
 390      * expressed in "permission".
 391      *
 392      * @param permission the Permission object to compare
 393      *
 394      * @return true if "permission" is a proper subset of a permission in
 395      * the set, false if not.
 396      */
 397     public boolean implies(Permission permission) {
 398         if (! (permission instanceof BasicPermission))
 399             return false;
 400 
 401         BasicPermission bp = (BasicPermission) permission;
 402 
 403         // random subclasses of BasicPermission do not imply each other
 404         if (bp.getClass() != permClass)
 405             return false;
 406 
 407         // short circuit if the "*" Permission was added
 408         if (all_allowed)
 409             return true;
 410 
 411         // strategy:
 412         // Check for full match first. Then work our way up the
 413         // path looking for matches on a.b..*
 414 
 415         String path = bp.getCanonicalName();
 416         //System.out.println("check "+path);
 417 
 418         Permission x;
 419 
 420         synchronized (this) {
 421             x = perms.get(path);
 422         }
 423 
 424         if (x != null) {
 425             // we have a direct hit!
 426             return x.implies(permission);
 427         }
 428 
 429         // work our way up the tree...
 430         int last, offset;
 431 
 432         offset = path.length()-1;
 433 
 434         while ((last = path.lastIndexOf(".", offset)) != -1) {
 435 
 436             path = path.substring(0, last+1) + "*";
 437             //System.out.println("check "+path);
 438 
 439             synchronized (this) {
 440                 x = perms.get(path);
 441             }
 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     public Enumeration<Permission> elements() {
 461         // Convert Iterator of Map values into an Enumeration
 462         synchronized (this) {
 463             return Collections.enumeration(perms.values());
 464         }
 465     }
 466 
 467     // Need to maintain serialization interoperability with earlier releases,
 468     // which had the serializable field:
 469     //
 470     // @serial the Hashtable is indexed by the BasicPermission name
 471     //
 472     // private Hashtable permissions;
 473     /**
 474      * @serialField permissions java.util.Hashtable
 475      *    The BasicPermissions in this BasicPermissionCollection.
 476      *    All BasicPermissions in the collection must belong to the same class.
 477      *    The Hashtable is indexed by the BasicPermission name; the value
 478      *    of the Hashtable entry is the permission.
 479      * @serialField all_allowed boolean
 480      *   This is set to <code>true</code> if this BasicPermissionCollection
 481      *   contains a BasicPermission with '*' as its permission name.
 482      * @serialField permClass java.lang.Class
 483      *   The class to which all BasicPermissions in this
 484      *   BasicPermissionCollection belongs.
 485      */
 486     private static final ObjectStreamField[] serialPersistentFields = {
 487         new ObjectStreamField("permissions", Hashtable.class),
 488         new ObjectStreamField("all_allowed", Boolean.TYPE),
 489         new ObjectStreamField("permClass", Class.class),
 490     };
 491 
 492     /**
 493      * @serialData Default fields.
 494      */
 495     /*
 496      * Writes the contents of the perms field out as a Hashtable for
 497      * serialization compatibility with earlier releases. all_allowed
 498      * and permClass unchanged.
 499      */
 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         synchronized (this) {
 508             permissions.putAll(perms);
 509         }
 510 
 511         // Write out serializable fields
 512         ObjectOutputStream.PutField pfields = out.putFields();
 513         pfields.put("all_allowed", all_allowed);
 514         pfields.put("permissions", permissions);
 515         pfields.put("permClass", permClass);
 516         out.writeFields();
 517     }
 518 
 519     /**
 520      * readObject is called to restore the state of the
 521      * BasicPermissionCollection from a stream.
 522      */
 523     private void readObject(java.io.ObjectInputStream in)
 524          throws IOException, ClassNotFoundException
 525     {
 526         // Don't call defaultReadObject()
 527 
 528         // Read in serialized fields
 529         ObjectInputStream.GetField gfields = in.readFields();
 530 
 531         // Get permissions
 532         // writeObject writes a Hashtable<String, Permission> for the
 533         // permissions key, so this cast is safe, unless the data is corrupt.
 534         @SuppressWarnings("unchecked")
 535         Hashtable<String, Permission> permissions =
 536                 (Hashtable<String, Permission>)gfields.get("permissions", null);
 537         perms = new HashMap<String, Permission>(permissions.size()*2);
 538         perms.putAll(permissions);
 539 
 540         // Get all_allowed
 541         all_allowed = gfields.get("all_allowed", false);
 542 
 543         // Get permClass
 544         permClass = (Class<?>) gfields.get("permClass", null);
 545 
 546         if (permClass == null) {
 547             // set permClass
 548             Enumeration<Permission> e = permissions.elements();
 549             if (e.hasMoreElements()) {
 550                 Permission p = e.nextElement();
 551                 permClass = p.getClass();
 552             }
 553         }
 554     }
 555 }