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 }