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