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