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