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