1 /*
   2  * Copyright (c) 1997, 2016, 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.lang.ref.Reference;
  29 import java.lang.ref.ReferenceQueue;
  30 import java.lang.ref.SoftReference;
  31 import java.lang.ref.WeakReference;
  32 import java.util.ArrayList;
  33 import java.util.Enumeration;
  34 import java.util.List;
  35 import java.util.concurrent.ConcurrentHashMap;
  36 import jdk.internal.misc.JavaSecurityAccess;
  37 import jdk.internal.misc.JavaSecurityProtectionDomainAccess;
  38 import static jdk.internal.misc.JavaSecurityProtectionDomainAccess.ProtectionDomainCache;
  39 import jdk.internal.misc.SharedSecrets;
  40 import sun.security.provider.PolicyFile;
  41 import sun.security.util.Debug;
  42 import sun.security.util.FilePermCompat;
  43 import sun.security.util.SecurityConstants;
  44 
  45 /**
  46  * The ProtectionDomain class encapsulates the characteristics of a domain,
  47  * which encloses a set of classes whose instances are granted a set
  48  * of permissions when being executed on behalf of a given set of Principals.
  49  * <p>
  50  * A static set of permissions can be bound to a ProtectionDomain when it is
  51  * constructed; such permissions are granted to the domain regardless of the
  52  * Policy in force. However, to support dynamic security policies, a
  53  * ProtectionDomain can also be constructed such that it is dynamically
  54  * mapped to a set of permissions by the current Policy whenever a permission
  55  * is checked.
  56  *
  57  * @author Li Gong
  58  * @author Roland Schemers
  59  * @author Gary Ellison
  60  */
  61 
  62 public class ProtectionDomain {
  63 
  64     private static class JavaSecurityAccessImpl implements JavaSecurityAccess {
  65 
  66         private JavaSecurityAccessImpl() {
  67         }
  68 
  69         @Override
  70         public <T> T doIntersectionPrivilege(
  71                 PrivilegedAction<T> action,
  72                 final AccessControlContext stack,
  73                 final AccessControlContext context) {
  74             if (action == null) {
  75                 throw new NullPointerException();
  76             }
  77 
  78             return AccessController.doPrivileged(
  79                 action,
  80                 getCombinedACC(context, stack)
  81             );
  82         }
  83 
  84         @Override
  85         public <T> T doIntersectionPrivilege(
  86                 PrivilegedAction<T> action,
  87                 AccessControlContext context) {
  88             return doIntersectionPrivilege(action,
  89                 AccessController.getContext(), context);
  90         }
  91 
  92         @Override
  93         public ProtectionDomain[] getProtectDomains(AccessControlContext context) {
  94             return context.getContext();
  95         }
  96 
  97         private static AccessControlContext getCombinedACC(
  98             AccessControlContext context, AccessControlContext stack) {
  99             AccessControlContext acc =
 100                 new AccessControlContext(context, stack.getCombiner(), true);
 101 
 102             return new AccessControlContext(stack.getContext(), acc).optimize();
 103         }
 104     }
 105 
 106     static {
 107         // setup SharedSecrets to allow access to doIntersectionPrivilege
 108         // methods and ProtectionDomain cache
 109         SharedSecrets.setJavaSecurityAccess(new JavaSecurityAccessImpl());
 110         SharedSecrets.setJavaSecurityProtectionDomainAccess(
 111             new JavaSecurityProtectionDomainAccess() {
 112                 @Override
 113                 public ProtectionDomainCache getProtectionDomainCache() {
 114                     return new PDCache();
 115                 }
 116             });
 117     }
 118 
 119     /**
 120      * Used for storing ProtectionDomains as keys in a Map.
 121      */
 122     static final class Key {}
 123 
 124     /* CodeSource */
 125     private CodeSource codesource ;
 126 
 127     /* ClassLoader the protection domain was consed from */
 128     private ClassLoader classloader;
 129 
 130     /* Principals running-as within this protection domain */
 131     private Principal[] principals;
 132 
 133     /* the rights this protection domain is granted */
 134     private PermissionCollection permissions;
 135 
 136     /* if the permissions object has AllPermission */
 137     private boolean hasAllPerm = false;
 138 
 139     /* the PermissionCollection is static (pre 1.4 constructor)
 140        or dynamic (via a policy refresh) */
 141     private final boolean staticPermissions;
 142 
 143     /*
 144      * An object used as a key when the ProtectionDomain is stored in a Map.
 145      */
 146     final Key key = new Key();
 147 
 148     /**
 149      * Creates a new ProtectionDomain with the given CodeSource and
 150      * Permissions. If the permissions object is not null, then
 151      *  {@code setReadOnly()} will be called on the passed in
 152      * Permissions object.
 153      * <p>
 154      * The permissions granted to this domain are static, i.e.
 155      * invoking the {@link #staticPermissionsOnly()} method returns true.
 156      * They contain only the ones passed to this constructor and
 157      * the current Policy will not be consulted.
 158      *
 159      * @param codesource the codesource associated with this domain
 160      * @param permissions the permissions granted to this domain
 161      */
 162     public ProtectionDomain(CodeSource codesource,
 163                             PermissionCollection permissions) {
 164         this.codesource = codesource;
 165         if (permissions != null) {
 166             this.permissions = permissions;
 167             this.permissions.setReadOnly();
 168             if (permissions instanceof Permissions &&
 169                 ((Permissions)permissions).allPermission != null) {
 170                 hasAllPerm = true;
 171             }
 172         }
 173         this.classloader = null;
 174         this.principals = new Principal[0];
 175         staticPermissions = true;
 176     }
 177 
 178     /**
 179      * Creates a new ProtectionDomain qualified by the given CodeSource,
 180      * Permissions, ClassLoader and array of Principals. If the
 181      * permissions object is not null, then {@code setReadOnly()}
 182      * will be called on the passed in Permissions object.
 183      * <p>
 184      * The permissions granted to this domain are dynamic, i.e.
 185      * invoking the {@link #staticPermissionsOnly()} method returns false.
 186      * They include both the static permissions passed to this constructor,
 187      * and any permissions granted to this domain by the current Policy at the
 188      * time a permission is checked.
 189      * <p>
 190      * This constructor is typically used by
 191      * {@link SecureClassLoader ClassLoaders}
 192      * and {@link DomainCombiner DomainCombiners} which delegate to
 193      * {@code Policy} to actively associate the permissions granted to
 194      * this domain. This constructor affords the
 195      * Policy provider the opportunity to augment the supplied
 196      * PermissionCollection to reflect policy changes.
 197      *
 198      * @param codesource the CodeSource associated with this domain
 199      * @param permissions the permissions granted to this domain
 200      * @param classloader the ClassLoader associated with this domain
 201      * @param principals the array of Principals associated with this
 202      * domain. The contents of the array are copied to protect against
 203      * subsequent modification.
 204      * @see Policy#refresh
 205      * @see Policy#getPermissions(ProtectionDomain)
 206      * @since 1.4
 207      */
 208     public ProtectionDomain(CodeSource codesource,
 209                             PermissionCollection permissions,
 210                             ClassLoader classloader,
 211                             Principal[] principals) {
 212         this.codesource = codesource;
 213         if (permissions != null) {
 214             this.permissions = permissions;
 215             this.permissions.setReadOnly();
 216             if (permissions instanceof Permissions &&
 217                 ((Permissions)permissions).allPermission != null) {
 218                 hasAllPerm = true;
 219             }
 220         }
 221         this.classloader = classloader;
 222         this.principals = (principals != null ? principals.clone():
 223                            new Principal[0]);
 224         staticPermissions = false;
 225     }
 226 
 227     /**
 228      * Returns the CodeSource of this domain.
 229      * @return the CodeSource of this domain which may be null.
 230      * @since 1.2
 231      */
 232     public final CodeSource getCodeSource() {
 233         return this.codesource;
 234     }
 235 
 236 
 237     /**
 238      * Returns the ClassLoader of this domain.
 239      * @return the ClassLoader of this domain which may be null.
 240      *
 241      * @since 1.4
 242      */
 243     public final ClassLoader getClassLoader() {
 244         return this.classloader;
 245     }
 246 
 247 
 248     /**
 249      * Returns an array of principals for this domain.
 250      * @return a non-null array of principals for this domain.
 251      * Returns a new array each time this method is called.
 252      *
 253      * @since 1.4
 254      */
 255     public final Principal[] getPrincipals() {
 256         return this.principals.clone();
 257     }
 258 
 259     /**
 260      * Returns the static permissions granted to this domain.
 261      *
 262      * @return the static set of permissions for this domain which may be null.
 263      * @see Policy#refresh
 264      * @see Policy#getPermissions(ProtectionDomain)
 265      */
 266     public final PermissionCollection getPermissions() {
 267         return permissions;
 268     }
 269 
 270     /**
 271      * Returns true if this domain contains only static permissions
 272      * and does not check the current {@code Policy} at the time of
 273      * permission checking.
 274      *
 275      * @return true if this domain contains only static permissions.
 276      *
 277      * @since 9
 278      */
 279     public final boolean staticPermissionsOnly() {
 280         return this.staticPermissions;
 281     }
 282 
 283     /**
 284      * Check and see if this ProtectionDomain implies the permissions
 285      * expressed in the Permission object.
 286      * <p>
 287      * The set of permissions evaluated is a function of whether the
 288      * ProtectionDomain was constructed with a static set of permissions
 289      * or it was bound to a dynamically mapped set of permissions.
 290      * <p>
 291      * If the {@link #staticPermissionsOnly()} method returns
 292      * true, then the permission will only be checked against the
 293      * PermissionCollection supplied at construction.
 294      * <p>
 295      * Otherwise, the permission will be checked against the combination
 296      * of the PermissionCollection supplied at construction and
 297      * the current Policy binding.
 298      *
 299      * @param perm the Permission object to check.
 300      *
 301      * @return true if {@code perm} is implied by this ProtectionDomain.
 302      */
 303     public boolean implies(Permission perm) {
 304 
 305         if (hasAllPerm) {
 306             // internal permission collection already has AllPermission -
 307             // no need to go to policy
 308             return true;
 309         }
 310 
 311         if (!staticPermissions &&
 312             Policy.getPolicyNoCheck().implies(this, perm)) {
 313             return true;
 314         }
 315         if (permissions != null) {
 316             return permissions.implies(perm);
 317         }
 318 
 319         return false;
 320     }
 321 
 322     /**
 323      * This method has the same logic flow as {@link #implies} except that
 324      * when the {@link FilePermCompat#compat} flag is on it ensures
 325      * FilePermission compatibility after JDK-8164705. {@code implies()}
 326      * is called when compat flag is not on or user has extended
 327      * {@code ProtectionDomain}.
 328      *
 329      * This method is called by {@link AccessControlContext#checkPermission}
 330      * and not intended to be called by an application.
 331      */
 332     boolean impliesWithAltFilePerm(Permission perm) {
 333 
 334         // If this is a subclass of ProtectionDomain. Call the old method.
 335         if (!FilePermCompat.compat || getClass() != ProtectionDomain.class) {
 336             return implies(perm);
 337         }
 338 
 339         if (hasAllPerm) {
 340             // internal permission collection already has AllPermission -
 341             // no need to go to policy
 342             return true;
 343         }
 344 
 345         Permission p2 = null;
 346         boolean p2Calculated = false;
 347 
 348         if (!staticPermissions) {
 349             Policy policy = Policy.getPolicyNoCheck();
 350             if (policy instanceof PolicyFile) {
 351                 // The PolicyFile implementation supports compatibility
 352                 // inside and it also covers the static permissions.
 353                 return policy.implies(this, perm);
 354             } else {
 355                 if (policy.implies(this, perm)) {
 356                     return true;
 357                 }
 358                 p2 = FilePermCompat.newPermUsingAltPath(perm);
 359                 p2Calculated = true;
 360                 if (p2 != null && policy.implies(this, p2)) {
 361                     return true;
 362                 }
 363             }
 364         }
 365         if (permissions != null) {
 366             if (permissions.implies(perm)) {
 367                 return true;
 368             } else {
 369                 if (!p2Calculated) {
 370                     p2 = FilePermCompat.newPermUsingAltPath(perm);
 371                 }
 372                 if (p2 != null) {
 373                     return permissions.implies(p2);
 374                 }
 375             }
 376         }
 377         return false;
 378     }
 379 
 380     // called by the VM -- do not remove
 381     boolean impliesCreateAccessControlContext() {
 382         return implies(SecurityConstants.CREATE_ACC_PERMISSION);
 383     }
 384 
 385     /**
 386      * Convert a ProtectionDomain to a String.
 387      */
 388     @Override public String toString() {
 389         String pals = "<no principals>";
 390         if (principals != null && principals.length > 0) {
 391             StringBuilder palBuf = new StringBuilder("(principals ");
 392 
 393             for (int i = 0; i < principals.length; i++) {
 394                 palBuf.append(principals[i].getClass().getName() +
 395                             " \"" + principals[i].getName() +
 396                             "\"");
 397                 if (i < principals.length-1)
 398                     palBuf.append(",\n");
 399                 else
 400                     palBuf.append(")\n");
 401             }
 402             pals = palBuf.toString();
 403         }
 404 
 405         // Check if policy is set; we don't want to load
 406         // the policy prematurely here
 407         PermissionCollection pc = Policy.isSet() && seeAllp() ?
 408                                       mergePermissions():
 409                                       getPermissions();
 410 
 411         return "ProtectionDomain "+
 412             " "+codesource+"\n"+
 413             " "+classloader+"\n"+
 414             " "+pals+"\n"+
 415             " "+pc+"\n";
 416     }
 417 
 418     /*
 419      * holder class for the static field "debug" to delay its initialization
 420      */
 421     private static class DebugHolder {
 422         private static final Debug debug = Debug.getInstance("domain");
 423     }
 424 
 425     /**
 426      * Return true (merge policy permissions) in the following cases:
 427      *
 428      * . SecurityManager is null
 429      *
 430      * . SecurityManager is not null,
 431      *          debug is not null,
 432      *          SecurityManager impelmentation is in bootclasspath,
 433      *          Policy implementation is in bootclasspath
 434      *          (the bootclasspath restrictions avoid recursion)
 435      *
 436      * . SecurityManager is not null,
 437      *          debug is null,
 438      *          caller has Policy.getPolicy permission
 439      */
 440     private static boolean seeAllp() {
 441         SecurityManager sm = System.getSecurityManager();
 442 
 443         if (sm == null) {
 444             return true;
 445         } else {
 446             if (DebugHolder.debug != null) {
 447                 if (sm.getClass().getClassLoader() == null &&
 448                     Policy.getPolicyNoCheck().getClass().getClassLoader()
 449                                                                 == null) {
 450                     return true;
 451                 }
 452             } else {
 453                 try {
 454                     sm.checkPermission(SecurityConstants.GET_POLICY_PERMISSION);
 455                     return true;
 456                 } catch (SecurityException se) {
 457                     // fall thru and return false
 458                 }
 459             }
 460         }
 461 
 462         return false;
 463     }
 464 
 465     private PermissionCollection mergePermissions() {
 466         if (staticPermissions)
 467             return permissions;
 468 
 469         PermissionCollection perms =
 470             java.security.AccessController.doPrivileged
 471             (new java.security.PrivilegedAction<>() {
 472                     public PermissionCollection run() {
 473                         Policy p = Policy.getPolicyNoCheck();
 474                         return p.getPermissions(ProtectionDomain.this);
 475                     }
 476                 });
 477 
 478         Permissions mergedPerms = new Permissions();
 479         int swag = 32;
 480         int vcap = 8;
 481         Enumeration<Permission> e;
 482         List<Permission> pdVector = new ArrayList<>(vcap);
 483         List<Permission> plVector = new ArrayList<>(swag);
 484 
 485         //
 486         // Build a vector of domain permissions for subsequent merge
 487         if (permissions != null) {
 488             synchronized (permissions) {
 489                 e = permissions.elements();
 490                 while (e.hasMoreElements()) {
 491                     pdVector.add(e.nextElement());
 492                 }
 493             }
 494         }
 495 
 496         //
 497         // Build a vector of Policy permissions for subsequent merge
 498         if (perms != null) {
 499             synchronized (perms) {
 500                 e = perms.elements();
 501                 while (e.hasMoreElements()) {
 502                     plVector.add(e.nextElement());
 503                     vcap++;
 504                 }
 505             }
 506         }
 507 
 508         if (perms != null && permissions != null) {
 509             //
 510             // Weed out the duplicates from the policy. Unless a refresh
 511             // has occurred since the pd was consed this should result in
 512             // an empty vector.
 513             synchronized (permissions) {
 514                 e = permissions.elements();   // domain vs policy
 515                 while (e.hasMoreElements()) {
 516                     Permission pdp = e.nextElement();
 517                     Class<?> pdpClass = pdp.getClass();
 518                     String pdpActions = pdp.getActions();
 519                     String pdpName = pdp.getName();
 520                     for (int i = 0; i < plVector.size(); i++) {
 521                         Permission pp = plVector.get(i);
 522                         if (pdpClass.isInstance(pp)) {
 523                             // The equals() method on some permissions
 524                             // have some side effects so this manual
 525                             // comparison is sufficient.
 526                             if (pdpName.equals(pp.getName()) &&
 527                                 pdpActions.equals(pp.getActions())) {
 528                                 plVector.remove(i);
 529                                 break;
 530                             }
 531                         }
 532                     }
 533                 }
 534             }
 535         }
 536 
 537         if (perms !=null) {
 538             // the order of adding to merged perms and permissions
 539             // needs to preserve the bugfix 4301064
 540 
 541             for (int i = plVector.size()-1; i >= 0; i--) {
 542                 mergedPerms.add(plVector.get(i));
 543             }
 544         }
 545         if (permissions != null) {
 546             for (int i = pdVector.size()-1; i >= 0; i--) {
 547                 mergedPerms.add(pdVector.get(i));
 548             }
 549         }
 550 
 551         return mergedPerms;
 552     }
 553 
 554     /**
 555      * A cache of ProtectionDomains and their Permissions.
 556      *
 557      * This class stores ProtectionDomains as weak keys in a ConcurrentHashMap
 558      * with additional support for checking and removing weak keys that are no
 559      * longer in use. There can be cases where the permission collection may
 560      * have a chain of strong references back to the ProtectionDomain, which
 561      * ordinarily would prevent the entry from being removed from the map. To
 562      * address that, we wrap the permission collection in a SoftReference so
 563      * that it can be reclaimed by the garbage collector due to memory demand.
 564      */
 565     private static class PDCache implements ProtectionDomainCache {
 566         private final ConcurrentHashMap<WeakProtectionDomainKey,
 567                                         SoftReference<PermissionCollection>>
 568                                         pdMap = new ConcurrentHashMap<>();
 569         private final ReferenceQueue<Key> queue = new ReferenceQueue<>();
 570 
 571         @Override
 572         public void put(ProtectionDomain pd, PermissionCollection pc) {
 573             processQueue(queue, pdMap);
 574             WeakProtectionDomainKey weakPd =
 575                 new WeakProtectionDomainKey(pd, queue);
 576             pdMap.put(weakPd, new SoftReference<>(pc));
 577         }
 578 
 579         @Override
 580         public PermissionCollection get(ProtectionDomain pd) {
 581             processQueue(queue, pdMap);
 582             WeakProtectionDomainKey weakPd = new WeakProtectionDomainKey(pd);
 583             SoftReference<PermissionCollection> sr = pdMap.get(weakPd);
 584             return (sr == null) ? null : sr.get();
 585         }
 586 
 587         /**
 588          * Removes weak keys from the map that have been enqueued
 589          * on the reference queue and are no longer in use.
 590          */
 591         private static void processQueue(ReferenceQueue<Key> queue,
 592                                          ConcurrentHashMap<? extends
 593                                          WeakReference<Key>, ?> pdMap) {
 594             Reference<? extends Key> ref;
 595             while ((ref = queue.poll()) != null) {
 596                 pdMap.remove(ref);
 597             }
 598         }
 599     }
 600 
 601     /**
 602      * A weak key for a ProtectionDomain.
 603      */
 604     private static class WeakProtectionDomainKey extends WeakReference<Key> {
 605         /**
 606          * Saved value of the referent's identity hash code, to maintain
 607          * a consistent hash code after the referent has been cleared
 608          */
 609         private final int hash;
 610 
 611         /**
 612          * A key representing a null ProtectionDomain.
 613          */
 614         private static final Key NULL_KEY = new Key();
 615 
 616         /**
 617          * Create a new WeakProtectionDomain with the specified domain and
 618          * registered with a queue.
 619          */
 620         WeakProtectionDomainKey(ProtectionDomain pd, ReferenceQueue<Key> rq) {
 621             this((pd == null ? NULL_KEY : pd.key), rq);
 622         }
 623 
 624         WeakProtectionDomainKey(ProtectionDomain pd) {
 625             this(pd == null ? NULL_KEY : pd.key);
 626         }
 627 
 628         private WeakProtectionDomainKey(Key key, ReferenceQueue<Key> rq) {
 629             super(key, rq);
 630             hash = key.hashCode();
 631         }
 632 
 633         private WeakProtectionDomainKey(Key key) {
 634             super(key);
 635             hash = key.hashCode();
 636         }
 637 
 638         /**
 639          * Returns the identity hash code of the original referent.
 640          */
 641         @Override
 642         public int hashCode() {
 643             return hash;
 644         }
 645 
 646         /**
 647          * Returns true if the given object is an identical
 648          * WeakProtectionDomainKey instance, or, if this object's referent
 649          * has not been cleared and the given object is another
 650          * WeakProtectionDomainKey instance with an identical non-null
 651          * referent as this one.
 652          */
 653         @Override
 654         public boolean equals(Object obj) {
 655             if (obj == this) {
 656                 return true;
 657             }
 658 
 659             if (obj instanceof WeakProtectionDomainKey) {
 660                 Object referent = get();
 661                 return (referent != null) &&
 662                        (referent == ((WeakProtectionDomainKey)obj).get());
 663             } else {
 664                 return false;
 665             }
 666         }
 667     }
 668 }