1 /*
   2  * Copyright (c) 1997, 2013, 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.Hashtable;
  30 import java.util.NoSuchElementException;
  31 import java.util.Map;
  32 import java.util.HashMap;
  33 import java.util.List;
  34 import java.util.Iterator;
  35 import java.util.Collections;
  36 import java.io.Serializable;
  37 import java.io.ObjectStreamField;
  38 import java.io.ObjectOutputStream;
  39 import java.io.ObjectInputStream;
  40 import java.io.IOException;
  41 
  42 
  43 /**
  44  * This class represents a heterogeneous collection of Permissions. That is,
  45  * it contains different types of Permission objects, organized into
  46  * PermissionCollections. For example, if any
  47  * {@code java.io.FilePermission} objects are added to an instance of
  48  * this class, they are all stored in a single
  49  * PermissionCollection. It is the PermissionCollection returned by a call to
  50  * the {@code newPermissionCollection} method in the FilePermission class.
  51  * Similarly, any {@code java.lang.RuntimePermission} objects are
  52  * stored in the PermissionCollection returned by a call to the
  53  * {@code newPermissionCollection} method in the
  54  * RuntimePermission class. Thus, this class represents a collection of
  55  * PermissionCollections.
  56  *
  57  * <p>When the {@code add} method is called to add a Permission, the
  58  * Permission is stored in the appropriate PermissionCollection. If no such
  59  * collection exists yet, the Permission object's class is determined and the
  60  * {@code newPermissionCollection} method is called on that class to create
  61  * the PermissionCollection and add it to the Permissions object. If
  62  * {@code newPermissionCollection} returns null, then a default
  63  * PermissionCollection that uses a hashtable will be created and used. Each
  64  * hashtable entry stores a Permission object as both the key and the value.
  65  *
  66  * <p> Enumerations returned via the {@code elements} method are
  67  * not <em>fail-fast</em>.  Modifications to a collection should not be
  68  * performed while enumerating over that collection.
  69  *
  70  * @see Permission
  71  * @see PermissionCollection
  72  * @see AllPermission
  73  *
  74  *
  75  * @author Marianne Mueller
  76  * @author Roland Schemers
  77  *
  78  * @serial exclude
  79  */
  80 
  81 public final class Permissions extends PermissionCollection
  82 implements Serializable
  83 {
  84     /**
  85      * Key is permissions Class, value is PermissionCollection for that class.
  86      * Not serialized; see serialization section at end of class.
  87      */
  88     private transient Map<Class<?>, PermissionCollection> permsMap;
  89 
  90     // optimization. keep track of whether unresolved permissions need to be
  91     // checked
  92     private transient boolean hasUnresolved = false;
  93 
  94     // optimization. keep track of the AllPermission collection
  95     // - package private for ProtectionDomain optimization
  96     PermissionCollection allPermission;
  97 
  98     /**
  99      * Creates a new Permissions object containing no PermissionCollections.
 100      */
 101     public Permissions() {
 102         permsMap = new HashMap<Class<?>, PermissionCollection>(11);
 103         allPermission = null;
 104     }
 105 
 106     /**
 107      * Adds a permission object to the PermissionCollection for the class the
 108      * permission belongs to. For example, if <i>permission</i> is a
 109      * FilePermission, it is added to the FilePermissionCollection stored
 110      * in this Permissions object.
 111      *
 112      * This method creates
 113      * a new PermissionCollection object (and adds the permission to it)
 114      * if an appropriate collection does not yet exist. <p>
 115      *
 116      * @param permission the Permission object to add.
 117      *
 118      * @exception SecurityException if this Permissions object is
 119      * marked as readonly.
 120      *
 121      * @see PermissionCollection#isReadOnly()
 122      */
 123 
 124     public void add(Permission permission) {
 125         if (isReadOnly())
 126             throw new SecurityException(
 127               "attempt to add a Permission to a readonly Permissions object");
 128 
 129         PermissionCollection pc;
 130 
 131         synchronized (this) {
 132             pc = getPermissionCollection(permission, true);
 133             pc.add(permission);
 134         }
 135 
 136         // No sync; staleness -> optimizations delayed, which is OK
 137         if (permission instanceof AllPermission) {
 138             allPermission = pc;
 139         }
 140         if (permission instanceof UnresolvedPermission) {
 141             hasUnresolved = true;
 142         }
 143     }
 144 
 145     /**
 146      * Checks to see if this object's PermissionCollection for permissions of
 147      * the specified permission's class implies the permissions
 148      * expressed in the <i>permission</i> object. Returns true if the
 149      * combination of permissions in the appropriate PermissionCollection
 150      * (e.g., a FilePermissionCollection for a FilePermission) together
 151      * imply the specified permission.
 152      *
 153      * <p>For example, suppose there is a FilePermissionCollection in this
 154      * Permissions object, and it contains one FilePermission that specifies
 155      * "read" access for  all files in all subdirectories of the "/tmp"
 156      * directory, and another FilePermission that specifies "write" access
 157      * for all files in the "/tmp/scratch/foo" directory.
 158      * Then if the {@code implies} method
 159      * is called with a permission specifying both "read" and "write" access
 160      * to files in the "/tmp/scratch/foo" directory, {@code true} is
 161      * returned.
 162      *
 163      * <p>Additionally, if this PermissionCollection contains the
 164      * AllPermission, this method will always return true.
 165      * <p>
 166      * @param permission the Permission object to check.
 167      *
 168      * @return true if "permission" is implied by the permissions in the
 169      * PermissionCollection it
 170      * belongs to, false if not.
 171      */
 172 
 173     public boolean implies(Permission permission) {
 174         // No sync; staleness -> skip optimization, which is OK
 175         if (allPermission != null) {
 176             return true; // AllPermission has already been added
 177         } else {
 178             synchronized (this) {
 179                 PermissionCollection pc = getPermissionCollection(permission,
 180                     false);
 181                 if (pc != null) {
 182                     return pc.implies(permission);
 183                 } else {
 184                     // none found
 185                     return false;
 186                 }
 187             }
 188         }
 189     }
 190 
 191     /**
 192      * Returns an enumeration of all the Permission objects in all the
 193      * PermissionCollections in this Permissions object.
 194      *
 195      * @return an enumeration of all the Permissions.
 196      */
 197 
 198     public Enumeration<Permission> elements() {
 199         // go through each Permissions in the hash table
 200         // and call their elements() function.
 201 
 202         synchronized (this) {
 203             return new PermissionsEnumerator(permsMap.values().iterator());
 204         }
 205     }
 206 
 207     /**
 208      * Gets the PermissionCollection in this Permissions object for
 209      * permissions whose type is the same as that of <i>p</i>.
 210      * For example, if <i>p</i> is a FilePermission,
 211      * the FilePermissionCollection
 212      * stored in this Permissions object will be returned.
 213      *
 214      * If createEmpty is true,
 215      * this method creates a new PermissionCollection object for the specified
 216      * type of permission objects if one does not yet exist.
 217      * To do so, it first calls the {@code newPermissionCollection} method
 218      * on <i>p</i>.  Subclasses of class Permission
 219      * override that method if they need to store their permissions in a
 220      * particular PermissionCollection object in order to provide the
 221      * correct semantics when the {@code PermissionCollection.implies}
 222      * method is called.
 223      * If the call returns a PermissionCollection, that collection is stored
 224      * in this Permissions object. If the call returns null and createEmpty
 225      * is true, then
 226      * this method instantiates and stores a default PermissionCollection
 227      * that uses a hashtable to store its permission objects.
 228      *
 229      * createEmpty is ignored when creating empty PermissionCollection
 230      * for unresolved permissions because of the overhead of determining the
 231      * PermissionCollection to use.
 232      *
 233      * createEmpty should be set to false when this method is invoked from
 234      * implies() because it incurs the additional overhead of creating and
 235      * adding an empty PermissionCollection that will just return false.
 236      * It should be set to true when invoked from add().
 237      */
 238     private PermissionCollection getPermissionCollection(Permission p,
 239         boolean createEmpty) {
 240         Class<?> c = p.getClass();
 241 
 242         PermissionCollection pc = permsMap.get(c);
 243 
 244         if (!hasUnresolved && !createEmpty) {
 245             return pc;
 246         } else if (pc == null) {
 247 
 248             // Check for unresolved permissions
 249             pc = (hasUnresolved ? getUnresolvedPermissions(p) : null);
 250 
 251             // if still null, create a new collection
 252             if (pc == null && createEmpty) {
 253 
 254                 pc = p.newPermissionCollection();
 255 
 256                 // still no PermissionCollection?
 257                 // We'll give them a PermissionsHash.
 258                 if (pc == null)
 259                     pc = new PermissionsHash();
 260             }
 261 
 262             if (pc != null) {
 263                 permsMap.put(c, pc);
 264             }
 265         }
 266         return pc;
 267     }
 268 
 269     /**
 270      * Resolves any unresolved permissions of type p.
 271      *
 272      * @param p the type of unresolved permission to resolve
 273      *
 274      * @return PermissionCollection containing the unresolved permissions,
 275      *  or null if there were no unresolved permissions of type p.
 276      *
 277      */
 278     private PermissionCollection getUnresolvedPermissions(Permission p)
 279     {
 280         // Called from within synchronized method so permsMap doesn't need lock
 281 
 282         UnresolvedPermissionCollection uc =
 283         (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class);
 284 
 285         // we have no unresolved permissions if uc is null
 286         if (uc == null)
 287             return null;
 288 
 289         List<UnresolvedPermission> unresolvedPerms =
 290                                         uc.getUnresolvedPermissions(p);
 291 
 292         // we have no unresolved permissions of this type if unresolvedPerms is null
 293         if (unresolvedPerms == null)
 294             return null;
 295 
 296         java.security.cert.Certificate certs[] = null;
 297 
 298         Object signers[] = p.getClass().getSigners();
 299 
 300         int n = 0;
 301         if (signers != null) {
 302             for (int j=0; j < signers.length; j++) {
 303                 if (signers[j] instanceof java.security.cert.Certificate) {
 304                     n++;
 305                 }
 306             }
 307             certs = new java.security.cert.Certificate[n];
 308             n = 0;
 309             for (int j=0; j < signers.length; j++) {
 310                 if (signers[j] instanceof java.security.cert.Certificate) {
 311                     certs[n++] = (java.security.cert.Certificate)signers[j];
 312                 }
 313             }
 314         }
 315 
 316         PermissionCollection pc = null;
 317         synchronized (unresolvedPerms) {
 318             int len = unresolvedPerms.size();
 319             for (int i = 0; i < len; i++) {
 320                 UnresolvedPermission up = unresolvedPerms.get(i);
 321                 Permission perm = up.resolve(p, certs);
 322                 if (perm != null) {
 323                     if (pc == null) {
 324                         pc = p.newPermissionCollection();
 325                         if (pc == null)
 326                             pc = new PermissionsHash();
 327                     }
 328                     pc.add(perm);
 329                 }
 330             }
 331         }
 332         return pc;
 333     }
 334 
 335     private static final long serialVersionUID = 4858622370623524688L;
 336 
 337     // Need to maintain serialization interoperability with earlier releases,
 338     // which had the serializable field:
 339     // private Hashtable perms;
 340 
 341     /**
 342      * @serialField perms java.util.Hashtable
 343      *     A table of the Permission classes and PermissionCollections.
 344      * @serialField allPermission java.security.PermissionCollection
 345      */
 346     private static final ObjectStreamField[] serialPersistentFields = {
 347         new ObjectStreamField("perms", Hashtable.class),
 348         new ObjectStreamField("allPermission", PermissionCollection.class),
 349     };
 350 
 351     /**
 352      * @serialData Default fields.
 353      */
 354     /*
 355      * Writes the contents of the permsMap field out as a Hashtable for
 356      * serialization compatibility with earlier releases. allPermission
 357      * unchanged.
 358      */
 359     private void writeObject(ObjectOutputStream out) throws IOException {
 360         // Don't call out.defaultWriteObject()
 361 
 362         // Copy perms into a Hashtable
 363         Hashtable<Class<?>, PermissionCollection> perms =
 364             new Hashtable<>(permsMap.size()*2); // no sync; estimate
 365         synchronized (this) {
 366             perms.putAll(permsMap);
 367         }
 368 
 369         // Write out serializable fields
 370         ObjectOutputStream.PutField pfields = out.putFields();
 371 
 372         pfields.put("allPermission", allPermission); // no sync; staleness OK
 373         pfields.put("perms", perms);
 374         out.writeFields();
 375     }
 376 
 377     /*
 378      * Reads in a Hashtable of Class/PermissionCollections and saves them in the
 379      * permsMap field. Reads in allPermission.
 380      */
 381     private void readObject(ObjectInputStream in) throws IOException,
 382     ClassNotFoundException {
 383         // Don't call defaultReadObject()
 384 
 385         // Read in serialized fields
 386         ObjectInputStream.GetField gfields = in.readFields();
 387 
 388         // Get allPermission
 389         allPermission = (PermissionCollection) gfields.get("allPermission", null);
 390 
 391         // Get permissions
 392         // writeObject writes a Hashtable<Class<?>, PermissionCollection> for
 393         // the perms key, so this cast is safe, unless the data is corrupt.
 394         @SuppressWarnings("unchecked")
 395         Hashtable<Class<?>, PermissionCollection> perms =
 396             (Hashtable<Class<?>, PermissionCollection>)gfields.get("perms", null);
 397         permsMap = new HashMap<Class<?>, PermissionCollection>(perms.size()*2);
 398         permsMap.putAll(perms);
 399 
 400         // Set hasUnresolved
 401         UnresolvedPermissionCollection uc =
 402         (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class);
 403         hasUnresolved = (uc != null && uc.elements().hasMoreElements());
 404     }
 405 }
 406 
 407 final class PermissionsEnumerator implements Enumeration<Permission> {
 408 
 409     // all the perms
 410     private Iterator<PermissionCollection> perms;
 411     // the current set
 412     private Enumeration<Permission> permset;
 413 
 414     PermissionsEnumerator(Iterator<PermissionCollection> e) {
 415         perms = e;
 416         permset = getNextEnumWithMore();
 417     }
 418 
 419     // No need to synchronize; caller should sync on object as required
 420     public boolean hasMoreElements() {
 421         // if we enter with permissionimpl null, we know
 422         // there are no more left.
 423 
 424         if (permset == null)
 425             return  false;
 426 
 427         // try to see if there are any left in the current one
 428 
 429         if (permset.hasMoreElements())
 430             return true;
 431 
 432         // get the next one that has something in it...
 433         permset = getNextEnumWithMore();
 434 
 435         // if it is null, we are done!
 436         return (permset != null);
 437     }
 438 
 439     // No need to synchronize; caller should sync on object as required
 440     public Permission nextElement() {
 441 
 442         // hasMoreElements will update permset to the next permset
 443         // with something in it...
 444 
 445         if (hasMoreElements()) {
 446             return permset.nextElement();
 447         } else {
 448             throw new NoSuchElementException("PermissionsEnumerator");
 449         }
 450 
 451     }
 452 
 453     private Enumeration<Permission> getNextEnumWithMore() {
 454         while (perms.hasNext()) {
 455             PermissionCollection pc = perms.next();
 456             Enumeration<Permission> next =pc.elements();
 457             if (next.hasMoreElements())
 458                 return next;
 459         }
 460         return null;
 461 
 462     }
 463 }
 464 
 465 /**
 466  * A PermissionsHash stores a homogeneous set of permissions in a hashtable.
 467  *
 468  * @see Permission
 469  * @see Permissions
 470  *
 471  *
 472  * @author Roland Schemers
 473  *
 474  * @serial include
 475  */
 476 
 477 final class PermissionsHash extends PermissionCollection
 478 implements Serializable
 479 {
 480     /**
 481      * Key and value are (same) permissions objects.
 482      * Not serialized; see serialization section at end of class.
 483      */
 484     private transient Map<Permission, Permission> permsMap;
 485 
 486     /**
 487      * Create an empty PermissionsHash object.
 488      */
 489 
 490     PermissionsHash() {
 491         permsMap = new HashMap<Permission, Permission>(11);
 492     }
 493 
 494     /**
 495      * Adds a permission to the PermissionsHash.
 496      *
 497      * @param permission the Permission object to add.
 498      */
 499 
 500     public void add(Permission permission) {
 501         synchronized (this) {
 502             permsMap.put(permission, permission);
 503         }
 504     }
 505 
 506     /**
 507      * Check and see if this set of permissions implies the permissions
 508      * expressed in "permission".
 509      *
 510      * @param permission the Permission object to compare
 511      *
 512      * @return true if "permission" is a proper subset of a permission in
 513      * the set, false if not.
 514      */
 515 
 516     public boolean implies(Permission permission) {
 517         // attempt a fast lookup and implies. If that fails
 518         // then enumerate through all the permissions.
 519         synchronized (this) {
 520             Permission p = permsMap.get(permission);
 521 
 522             // If permission is found, then p.equals(permission)
 523             if (p == null) {
 524                 for (Permission p_ : permsMap.values()) {
 525                     if (p_.implies(permission))
 526                         return true;
 527                 }
 528                 return false;
 529             } else {
 530                 return true;
 531             }
 532         }
 533     }
 534 
 535     /**
 536      * Returns an enumeration of all the Permission objects in the container.
 537      *
 538      * @return an enumeration of all the Permissions.
 539      */
 540 
 541     public Enumeration<Permission> elements() {
 542         // Convert Iterator of Map values into an Enumeration
 543         synchronized (this) {
 544             return Collections.enumeration(permsMap.values());
 545         }
 546     }
 547 
 548     private static final long serialVersionUID = -8491988220802933440L;
 549     // Need to maintain serialization interoperability with earlier releases,
 550     // which had the serializable field:
 551     // private Hashtable perms;
 552     /**
 553      * @serialField perms java.util.Hashtable
 554      *     A table of the Permissions (both key and value are same).
 555      */
 556     private static final ObjectStreamField[] serialPersistentFields = {
 557         new ObjectStreamField("perms", Hashtable.class),
 558     };
 559 
 560     /**
 561      * @serialData Default fields.
 562      */
 563     /*
 564      * Writes the contents of the permsMap field out as a Hashtable for
 565      * serialization compatibility with earlier releases.
 566      */
 567     private void writeObject(ObjectOutputStream out) throws IOException {
 568         // Don't call out.defaultWriteObject()
 569 
 570         // Copy perms into a Hashtable
 571         Hashtable<Permission, Permission> perms =
 572                 new Hashtable<>(permsMap.size()*2);
 573         synchronized (this) {
 574             perms.putAll(permsMap);
 575         }
 576 
 577         // Write out serializable fields
 578         ObjectOutputStream.PutField pfields = out.putFields();
 579         pfields.put("perms", perms);
 580         out.writeFields();
 581     }
 582 
 583     /*
 584      * Reads in a Hashtable of Permission/Permission and saves them in the
 585      * permsMap field.
 586      */
 587     private void readObject(ObjectInputStream in) throws IOException,
 588     ClassNotFoundException {
 589         // Don't call defaultReadObject()
 590 
 591         // Read in serialized fields
 592         ObjectInputStream.GetField gfields = in.readFields();
 593 
 594         // Get permissions
 595         // writeObject writes a Hashtable<Class<?>, PermissionCollection> for
 596         // the perms key, so this cast is safe, unless the data is corrupt.
 597         @SuppressWarnings("unchecked")
 598         Hashtable<Permission, Permission> perms =
 599                 (Hashtable<Permission, Permission>)gfields.get("perms", null);
 600         permsMap = new HashMap<Permission, Permission>(perms.size()*2);
 601         permsMap.putAll(perms);
 602     }
 603 }