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