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