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.util.ArrayList; 29 import java.util.Collections; 30 import java.util.Enumeration; 31 import java.util.List; 32 import java.util.Map; 33 import java.util.Objects; 34 import java.util.WeakHashMap; 35 import jdk.internal.misc.JavaSecurityAccess; 36 import jdk.internal.misc.JavaSecurityProtectionDomainAccess; 37 import static jdk.internal.misc.JavaSecurityProtectionDomainAccess.ProtectionDomainCache; 38 import jdk.internal.misc.SharedSecrets; 39 import sun.security.action.GetPropertyAction; 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 * 47 *<p> 48 * This 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 // Set up JavaSecurityAccess in SharedSecrets 119 SharedSecrets.setJavaSecurityAccess(new JavaSecurityAccessImpl()); 120 } 121 122 /* CodeSource */ 123 private CodeSource codesource ; 124 125 /* ClassLoader the protection domain was consed from */ 126 private ClassLoader classloader; 127 128 /* Principals running-as within this protection domain */ 129 private Principal[] principals; 130 131 /* the rights this protection domain is granted */ 132 private PermissionCollection permissions; 133 134 /* if the permissions object has AllPermission */ 135 private boolean hasAllPerm = false; 136 137 /* the PermissionCollection is static (pre 1.4 constructor) 138 or dynamic (via a policy refresh) */ 139 private final boolean staticPermissions; 140 141 /* 142 * An object used as a key when the ProtectionDomain is stored in a Map. 143 */ 144 final Key key = new Key(); 145 146 /** 147 * Creates a new ProtectionDomain with the given CodeSource and 148 * Permissions. If the permissions object is not null, then 149 * {@code setReadOnly()} will be called on the passed in 150 * Permissions object. 151 * <p> 152 * The permissions granted to this domain are static, i.e. 153 * invoking the {@link #staticPermissionsOnly()} method returns true. 154 * They contain only the ones passed to this constructor and 155 * the current Policy will not be consulted. 156 * 157 * @param codesource the codesource associated with this domain 158 * @param permissions the permissions granted to this domain 159 */ 160 public ProtectionDomain(CodeSource codesource, 161 PermissionCollection permissions) { 162 this.codesource = codesource; 163 if (permissions != null) { 164 this.permissions = permissions; 165 this.permissions.setReadOnly(); 166 if (permissions instanceof Permissions && 167 ((Permissions)permissions).allPermission != null) { 168 hasAllPerm = true; 169 } 170 } 171 this.classloader = null; 172 this.principals = new Principal[0]; 173 staticPermissions = true; 174 } 175 176 /** 177 * Creates a new ProtectionDomain qualified by the given CodeSource, 178 * Permissions, ClassLoader and array of Principals. If the 179 * permissions object is not null, then {@code setReadOnly()} 180 * will be called on the passed in Permissions object. 181 * <p> 182 * The permissions granted to this domain are dynamic, i.e. 183 * invoking the {@link #staticPermissionsOnly()} method returns false. 184 * They include both the static permissions passed to this constructor, 185 * and any permissions granted to this domain by the current Policy at the 186 * time a permission is checked. 187 * <p> 188 * This constructor is typically used by 189 * {@link SecureClassLoader ClassLoaders} 190 * and {@link DomainCombiner DomainCombiners} which delegate to 191 * {@code Policy} to actively associate the permissions granted to 192 * this domain. This constructor affords the 193 * Policy provider the opportunity to augment the supplied 194 * PermissionCollection to reflect policy changes. 195 * 196 * @param codesource the CodeSource associated with this domain 197 * @param permissions the permissions granted to this domain 198 * @param classloader the ClassLoader associated with this domain 199 * @param principals the array of Principals associated with this 200 * domain. The contents of the array are copied to protect against 201 * subsequent modification. 202 * @see Policy#refresh 203 * @see Policy#getPermissions(ProtectionDomain) 204 * @since 1.4 205 */ 206 public ProtectionDomain(CodeSource codesource, 207 PermissionCollection permissions, 208 ClassLoader classloader, 209 Principal[] principals) { 210 this.codesource = codesource; 211 if (permissions != null) { 212 this.permissions = permissions; 213 this.permissions.setReadOnly(); 214 if (permissions instanceof Permissions && 215 ((Permissions)permissions).allPermission != null) { 216 hasAllPerm = true; 217 } 218 } 219 this.classloader = classloader; 220 this.principals = (principals != null ? principals.clone(): 221 new Principal[0]); 222 staticPermissions = false; 223 } 224 225 /** 226 * Returns the CodeSource of this domain. 227 * @return the CodeSource of this domain which may be null. 228 * @since 1.2 229 */ 230 public final CodeSource getCodeSource() { 231 return this.codesource; 232 } 233 234 235 /** 236 * Returns the ClassLoader of this domain. 237 * @return the ClassLoader of this domain which may be null. 238 * 239 * @since 1.4 240 */ 241 public final ClassLoader getClassLoader() { 242 return this.classloader; 243 } 244 245 246 /** 247 * Returns an array of principals for this domain. 248 * @return a non-null array of principals for this domain. 249 * Returns a new array each time this method is called. 250 * 251 * @since 1.4 252 */ 253 public final Principal[] getPrincipals() { 254 return this.principals.clone(); 255 } 256 257 /** 258 * Returns the static permissions granted to this domain. 259 * 260 * @return the static set of permissions for this domain which may be null. 261 * @see Policy#refresh 262 * @see Policy#getPermissions(ProtectionDomain) 263 */ 264 public final PermissionCollection getPermissions() { 265 return permissions; 266 } 267 268 /** 269 * Returns true if this domain contains only static permissions 270 * and does not check the current {@code Policy} at the time of 271 * permission checking. 272 * 273 * @return true if this domain contains only static permissions. 274 * 275 * @since 9 276 */ 277 public final boolean staticPermissionsOnly() { 278 return this.staticPermissions; 279 } 280 281 /** 282 * Check and see if this ProtectionDomain implies the permissions 283 * expressed in the Permission object. 284 * <p> 285 * The set of permissions evaluated is a function of whether the 286 * ProtectionDomain was constructed with a static set of permissions 287 * or it was bound to a dynamically mapped set of permissions. 288 * <p> 289 * If the {@link #staticPermissionsOnly()} method returns 290 * true, then the permission will only be checked against the 291 * PermissionCollection supplied at construction. 292 * <p> 293 * Otherwise, the permission will be checked against the combination 294 * of the PermissionCollection supplied at construction and 295 * the current Policy binding. 296 * 297 * @param perm the Permission object to check. 298 * 299 * @return true if {@code perm} is implied by this ProtectionDomain. 300 */ 301 public boolean implies(Permission perm) { 302 303 if (hasAllPerm) { 304 // internal permission collection already has AllPermission - 305 // no need to go to policy 306 return true; 307 } 308 309 if (!staticPermissions && 310 Policy.getPolicyNoCheck().implies(this, perm)) { 311 return true; 312 } 313 if (permissions != null) { 314 return permissions.implies(perm); 315 } 316 317 return false; 318 } 319 320 /** 321 * This method has almost the same logic flow as {@link #implies} but 322 * it ensures some level of FilePermission compatibility after JDK-8164705. 323 * 324 * This method is called by {@link AccessControlContext#checkPermission} 325 * and not intended to be called by an application. 326 */ 327 boolean impliesWithAltFilePerm(Permission perm) { 328 329 // If FilePermCompat.compat is set (default value), FilePermission 330 // checking compatibility should be considered. 331 332 // If filePermCompatInPD is set, this method checks for alternative 333 // FilePermission to keep compatibility for any Policy implementation. 334 // When set to false (default value), implies() is called since 335 // the PolicyFile implementation already supports compatibility. 336 337 // If this is a subclass of ProtectionDomain, call implies() 338 // because most likely user has overridden it. 339 340 if (!filePermCompatInPD || !FilePermCompat.compat || 341 getClass() != ProtectionDomain.class) { 342 return implies(perm); 343 } 344 345 if (hasAllPerm) { 346 // internal permission collection already has AllPermission - 347 // no need to go to policy 348 return true; 349 } 350 351 Permission p2 = null; 352 boolean p2Calculated = false; 353 354 if (!staticPermissions) { 355 Policy policy = Policy.getPolicyNoCheck(); 356 if (policy instanceof PolicyFile) { 357 // The PolicyFile implementation supports compatibility 358 // inside and it also covers the static permissions. 359 return policy.implies(this, perm); 360 } else { 361 if (policy.implies(this, perm)) { 362 return true; 363 } 364 p2 = FilePermCompat.newPermUsingAltPath(perm); 365 p2Calculated = true; 366 if (p2 != null && policy.implies(this, p2)) { 367 return true; 368 } 369 } 370 } 371 if (permissions != null) { 372 if (permissions.implies(perm)) { 373 return true; 374 } else { 375 if (!p2Calculated) { 376 p2 = FilePermCompat.newPermUsingAltPath(perm); 377 } 378 if (p2 != null) { 379 return permissions.implies(p2); 380 } 381 } 382 } 383 return false; 384 } 385 386 // called by the VM -- do not remove 387 boolean impliesCreateAccessControlContext() { 388 return implies(SecurityConstants.CREATE_ACC_PERMISSION); 389 } 390 391 /** 392 * Convert a ProtectionDomain to a String. 393 */ 394 @Override public String toString() { 395 String pals = "<no principals>"; 396 if (principals != null && principals.length > 0) { 397 StringBuilder palBuf = new StringBuilder("(principals "); 398 399 for (int i = 0; i < principals.length; i++) { 400 palBuf.append(principals[i].getClass().getName() + 401 " \"" + principals[i].getName() + 402 "\""); 403 if (i < principals.length-1) 404 palBuf.append(",\n"); 405 else 406 palBuf.append(")\n"); 407 } 408 pals = palBuf.toString(); 409 } 410 411 // Check if policy is set; we don't want to load 412 // the policy prematurely here 413 PermissionCollection pc = Policy.isSet() && seeAllp() ? 414 mergePermissions(): 415 getPermissions(); 416 417 return "ProtectionDomain "+ 418 " "+codesource+"\n"+ 419 " "+classloader+"\n"+ 420 " "+pals+"\n"+ 421 " "+pc+"\n"; 422 } 423 424 /* 425 * holder class for the static field "debug" to delay its initialization 426 */ 427 private static class DebugHolder { 428 private static final Debug debug = Debug.getInstance("domain"); 429 } 430 431 /** 432 * Return true (merge policy permissions) in the following cases: 433 * 434 * . SecurityManager is null 435 * 436 * . SecurityManager is not null, 437 * debug is not null, 438 * SecurityManager impelmentation is in bootclasspath, 439 * Policy implementation is in bootclasspath 440 * (the bootclasspath restrictions avoid recursion) 441 * 442 * . SecurityManager is not null, 443 * debug is null, 444 * caller has Policy.getPolicy permission 445 */ 446 private static boolean seeAllp() { 447 SecurityManager sm = System.getSecurityManager(); 448 449 if (sm == null) { 450 return true; 451 } else { 452 if (DebugHolder.debug != null) { 453 if (sm.getClass().getClassLoader() == null && 454 Policy.getPolicyNoCheck().getClass().getClassLoader() 455 == null) { 456 return true; 457 } 458 } else { 459 try { 460 sm.checkPermission(SecurityConstants.GET_POLICY_PERMISSION); 461 return true; 462 } catch (SecurityException se) { 463 // fall thru and return false 464 } 465 } 466 } 467 468 return false; 469 } 470 471 private PermissionCollection mergePermissions() { 472 if (staticPermissions) 473 return permissions; 474 475 PermissionCollection perms = 476 java.security.AccessController.doPrivileged 477 (new java.security.PrivilegedAction<>() { 478 public PermissionCollection run() { 479 Policy p = Policy.getPolicyNoCheck(); 480 return p.getPermissions(ProtectionDomain.this); 481 } 482 }); 483 484 Permissions mergedPerms = new Permissions(); 485 int swag = 32; 486 int vcap = 8; 487 Enumeration<Permission> e; 488 List<Permission> pdVector = new ArrayList<>(vcap); 489 List<Permission> plVector = new ArrayList<>(swag); 490 491 // 492 // Build a vector of domain permissions for subsequent merge 493 if (permissions != null) { 494 synchronized (permissions) { 495 e = permissions.elements(); 496 while (e.hasMoreElements()) { 497 pdVector.add(e.nextElement()); 498 } 499 } 500 } 501 502 // 503 // Build a vector of Policy permissions for subsequent merge 504 if (perms != null) { 505 synchronized (perms) { 506 e = perms.elements(); 507 while (e.hasMoreElements()) { 508 plVector.add(e.nextElement()); 509 vcap++; 510 } 511 } 512 } 513 514 if (perms != null && permissions != null) { 515 // 516 // Weed out the duplicates from the policy. Unless a refresh 517 // has occurred since the pd was consed this should result in 518 // an empty vector. 519 synchronized (permissions) { 520 e = permissions.elements(); // domain vs policy 521 while (e.hasMoreElements()) { 522 Permission pdp = e.nextElement(); 523 Class<?> pdpClass = pdp.getClass(); 524 String pdpActions = pdp.getActions(); 525 String pdpName = pdp.getName(); 526 for (int i = 0; i < plVector.size(); i++) { 527 Permission pp = plVector.get(i); 528 if (pdpClass.isInstance(pp)) { 529 // The equals() method on some permissions 530 // have some side effects so this manual 531 // comparison is sufficient. 532 if (pdpName.equals(pp.getName()) && 533 Objects.equals(pdpActions, pp.getActions())) { 534 plVector.remove(i); 535 break; 536 } 537 } 538 } 539 } 540 } 541 } 542 543 if (perms !=null) { 544 // the order of adding to merged perms and permissions 545 // needs to preserve the bugfix 4301064 546 547 for (int i = plVector.size()-1; i >= 0; i--) { 548 mergedPerms.add(plVector.get(i)); 549 } 550 } 551 if (permissions != null) { 552 for (int i = pdVector.size()-1; i >= 0; i--) { 553 mergedPerms.add(pdVector.get(i)); 554 } 555 } 556 557 return mergedPerms; 558 } 559 560 /** 561 * Used for storing ProtectionDomains as keys in a Map. 562 */ 563 final class Key {} 564 565 static { 566 SharedSecrets.setJavaSecurityProtectionDomainAccess( 567 new JavaSecurityProtectionDomainAccess() { 568 public ProtectionDomainCache getProtectionDomainCache() { 569 return new ProtectionDomainCache() { 570 private final Map<Key, PermissionCollection> map = 571 Collections.synchronizedMap 572 (new WeakHashMap<Key, PermissionCollection>()); 573 public void put(ProtectionDomain pd, 574 PermissionCollection pc) { 575 map.put((pd == null ? null : pd.key), pc); 576 } 577 public PermissionCollection get(ProtectionDomain pd) { 578 return pd == null ? map.get(null) : map.get(pd.key); 579 } 580 }; 581 } 582 }); 583 } 584 }