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