1 /* 2 * Copyright (c) 1997, 2013, 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.WeakHashMap; 34 import sun.misc.JavaSecurityProtectionDomainAccess; 35 import static sun.misc.JavaSecurityProtectionDomainAccess.ProtectionDomainCache; 36 import sun.security.util.Debug; 37 import sun.security.util.SecurityConstants; 38 import sun.misc.JavaSecurityAccess; 39 import sun.misc.SharedSecrets; 40 41 /** 42 * 43 *<p> 44 * This 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 * <p> 55 * 56 * @author Li Gong 57 * @author Roland Schemers 58 * @author Gary Ellison 59 */ 60 61 public class ProtectionDomain { 62 63 static { 64 // Set up JavaSecurityAccess in SharedSecrets 65 SharedSecrets.setJavaSecurityAccess( 66 new JavaSecurityAccess() { 67 public <T> T doIntersectionPrivilege( 68 PrivilegedAction<T> action, 69 final AccessControlContext stack, 70 final AccessControlContext context) 71 { 72 if (action == null) { 73 throw new NullPointerException(); 74 } 75 return AccessController.doPrivileged( 76 action, 77 new AccessControlContext( 78 stack.getContext(), context).optimize() 79 ); 80 } 81 82 public <T> T doIntersectionPrivilege( 83 PrivilegedAction<T> action, 84 AccessControlContext context) 85 { 86 return doIntersectionPrivilege(action, 87 AccessController.getContext(), context); 88 } 89 } 90 ); 91 } 92 93 /* CodeSource */ 94 private CodeSource codesource ; 95 96 /* ClassLoader the protection domain was consed from */ 97 private ClassLoader classloader; 98 99 /* Principals running-as within this protection domain */ 100 private Principal[] principals; 101 102 /* the rights this protection domain is granted */ 103 private PermissionCollection permissions; 104 105 /* if the permissions object has AllPermission */ 106 private boolean hasAllPerm = false; 107 108 /* the PermissionCollection is static (pre 1.4 constructor) 109 or dynamic (via a policy refresh) */ 110 private boolean staticPermissions; 111 112 /* 113 * An object used as a key when the ProtectionDomain is stored in a Map. 114 */ 115 final Key key = new Key(); 116 117 private static final Debug debug = Debug.getInstance("domain"); 118 119 /** 120 * Creates a new ProtectionDomain with the given CodeSource and 121 * Permissions. If the permissions object is not null, then 122 * {@code setReadOnly())} will be called on the passed in 123 * Permissions object. The only permissions granted to this domain 124 * are the ones specified; the current Policy will not be consulted. 125 * 126 * @param codesource the codesource associated with this domain 127 * @param permissions the permissions granted to this domain 128 */ 129 public ProtectionDomain(CodeSource codesource, 130 PermissionCollection permissions) { 131 this.codesource = codesource; 132 if (permissions != null) { 133 this.permissions = permissions; 134 this.permissions.setReadOnly(); 135 if (permissions instanceof Permissions && 136 ((Permissions)permissions).allPermission != null) { 137 hasAllPerm = true; 138 } 139 } 140 this.classloader = null; 141 this.principals = new Principal[0]; 142 staticPermissions = true; 143 } 144 145 /** 146 * Creates a new ProtectionDomain qualified by the given CodeSource, 147 * Permissions, ClassLoader and array of Principals. If the 148 * permissions object is not null, then {@code setReadOnly()} 149 * will be called on the passed in Permissions object. 150 * The permissions granted to this domain are dynamic; they include 151 * both the static permissions passed to this constructor, and any 152 * permissions granted to this domain by the current Policy at the 153 * time a permission is checked. 154 * <p> 155 * This constructor is typically used by 156 * {@link SecureClassLoader ClassLoaders} 157 * and {@link DomainCombiner DomainCombiners} which delegate to 158 * {@code Policy} to actively associate the permissions granted to 159 * this domain. This constructor affords the 160 * Policy provider the opportunity to augment the supplied 161 * PermissionCollection to reflect policy changes. 162 * <p> 163 * 164 * @param codesource the CodeSource associated with this domain 165 * @param permissions the permissions granted to this domain 166 * @param classloader the ClassLoader associated with this domain 167 * @param principals the array of Principals associated with this 168 * domain. The contents of the array are copied to protect against 169 * subsequent modification. 170 * @see Policy#refresh 171 * @see Policy#getPermissions(ProtectionDomain) 172 * @since 1.4 173 */ 174 public ProtectionDomain(CodeSource codesource, 175 PermissionCollection permissions, 176 ClassLoader classloader, 177 Principal[] principals) { 178 this.codesource = codesource; 179 if (permissions != null) { 180 this.permissions = permissions; 181 this.permissions.setReadOnly(); 182 if (permissions instanceof Permissions && 183 ((Permissions)permissions).allPermission != null) { 184 hasAllPerm = true; 185 } 186 } 187 this.classloader = classloader; 188 this.principals = (principals != null ? principals.clone(): 189 new Principal[0]); 190 staticPermissions = false; 191 } 192 193 /** 194 * Returns the CodeSource of this domain. 195 * @return the CodeSource of this domain which may be null. 196 * @since 1.2 197 */ 198 public final CodeSource getCodeSource() { 199 return this.codesource; 200 } 201 202 203 /** 204 * Returns the ClassLoader of this domain. 205 * @return the ClassLoader of this domain which may be null. 206 * 207 * @since 1.4 208 */ 209 public final ClassLoader getClassLoader() { 210 return this.classloader; 211 } 212 213 214 /** 215 * Returns an array of principals for this domain. 216 * @return a non-null array of principals for this domain. 217 * Returns a new array each time this method is called. 218 * 219 * @since 1.4 220 */ 221 public final Principal[] getPrincipals() { 222 return this.principals.clone(); 223 } 224 225 /** 226 * Returns the static permissions granted to this domain. 227 * 228 * @return the static set of permissions for this domain which may be null. 229 * @see Policy#refresh 230 * @see Policy#getPermissions(ProtectionDomain) 231 */ 232 public final PermissionCollection getPermissions() { 233 return permissions; 234 } 235 236 /** 237 * Check and see if this ProtectionDomain implies the permissions 238 * expressed in the Permission object. 239 * <p> 240 * The set of permissions evaluated is a function of whether the 241 * ProtectionDomain was constructed with a static set of permissions 242 * or it was bound to a dynamically mapped set of permissions. 243 * <p> 244 * If the ProtectionDomain was constructed to a 245 * {@link #ProtectionDomain(CodeSource, PermissionCollection) 246 * statically bound} PermissionCollection then the permission will 247 * only be checked against the PermissionCollection supplied at 248 * construction. 249 * <p> 250 * However, if the ProtectionDomain was constructed with 251 * the constructor variant which supports 252 * {@link #ProtectionDomain(CodeSource, PermissionCollection, 253 * ClassLoader, java.security.Principal[]) dynamically binding} 254 * permissions, then the permission will be checked against the 255 * combination of the PermissionCollection supplied at construction and 256 * the current Policy binding. 257 * <p> 258 * 259 * @param permission the Permission object to check. 260 * 261 * @return true if "permission" is implicit to this ProtectionDomain. 262 */ 263 public boolean implies(Permission permission) { 264 265 if (hasAllPerm) { 266 // internal permission collection already has AllPermission - 267 // no need to go to policy 268 return true; 269 } 270 271 if (!staticPermissions && 272 Policy.getPolicyNoCheck().implies(this, permission)) 273 return true; 274 if (permissions != null) 275 return permissions.implies(permission); 276 277 return false; 278 } 279 280 // called by the VM -- do not remove 281 boolean impliesCreateAccessControlContext() { 282 return implies(SecurityConstants.CREATE_ACC_PERMISSION); 283 } 284 285 /** 286 * Convert a ProtectionDomain to a String. 287 */ 288 @Override 289 public String toString() { 290 String pals = "<no principals>"; 291 if (principals != null && principals.length > 0) { 292 StringBuilder palBuilder = new StringBuilder("(principals "); 293 294 for (int i = 0; i < principals.length; i++) { 295 palBuilder.append(principals[i].getClass().getName()); 296 palBuilder.append(" \"").append(principals[i].getName()); 297 palBuilder.append('"'); 298 if (i < principals.length - 1) { 299 palBuilder.append(",\n"); 300 } else { 301 palBuilder.append(")\n"); 302 } 303 } 304 pals = palBuilder.toString(); 305 } 306 307 // Check if policy is set; we don't want to load 308 // the policy prematurely here 309 PermissionCollection pc = Policy.isSet() && seeAllp() ? mergePermissions() 310 : getPermissions(); 311 312 return "ProtectionDomain " + " " + codesource + "\n" + " " 313 + classloader + "\n" + " " + pals + "\n" + " " + pc + "\n"; 314 } 315 316 /** 317 * Return true (merge policy permissions) in the following cases: 318 * 319 * . SecurityManager is null 320 * 321 * . SecurityManager is not null, 322 * debug is not null, 323 * SecurityManager impelmentation is in bootclasspath, 324 * Policy implementation is in bootclasspath 325 * (the bootclasspath restrictions avoid recursion) 326 * 327 * . SecurityManager is not null, 328 * debug is null, 329 * caller has Policy.getPolicy permission 330 */ 331 private static boolean seeAllp() { 332 SecurityManager sm = System.getSecurityManager(); 333 334 if (sm == null) { 335 return true; 336 } else { 337 if (debug != null) { 338 if (sm.getClass().getClassLoader() == null && 339 Policy.getPolicyNoCheck().getClass().getClassLoader() 340 == null) { 341 return true; 342 } 343 } else { 344 try { 345 sm.checkPermission(SecurityConstants.GET_POLICY_PERMISSION); 346 return true; 347 } catch (SecurityException se) { 348 // fall thru and return false 349 } 350 } 351 } 352 353 return false; 354 } 355 356 private PermissionCollection mergePermissions() { 357 if (staticPermissions) 358 return permissions; 359 360 PermissionCollection perms = 361 java.security.AccessController.doPrivileged 362 (new java.security.PrivilegedAction<PermissionCollection>() { 363 public PermissionCollection run() { 364 Policy p = Policy.getPolicyNoCheck(); 365 return p.getPermissions(ProtectionDomain.this); 366 } 367 }); 368 369 Permissions mergedPerms = new Permissions(); 370 int swag = 32; 371 int vcap = 8; 372 Enumeration<Permission> e; 373 List<Permission> pdVector = new ArrayList<>(vcap); 374 List<Permission> plVector = new ArrayList<>(swag); 375 376 // 377 // Build a vector of domain permissions for subsequent merge 378 if (permissions != null) { 379 synchronized (permissions) { 380 e = permissions.elements(); 381 while (e.hasMoreElements()) { 382 pdVector.add(e.nextElement()); 383 } 384 } 385 } 386 387 // 388 // Build a vector of Policy permissions for subsequent merge 389 if (perms != null) { 390 synchronized (perms) { 391 e = perms.elements(); 392 while (e.hasMoreElements()) { 393 plVector.add(e.nextElement()); 394 vcap++; 395 } 396 } 397 } 398 399 if (perms != null && permissions != null) { 400 // 401 // Weed out the duplicates from the policy. Unless a refresh 402 // has occurred since the pd was consed this should result in 403 // an empty vector. 404 synchronized (permissions) { 405 e = permissions.elements(); // domain vs policy 406 while (e.hasMoreElements()) { 407 Permission pdp = e.nextElement(); 408 Class<?> pdpClass = pdp.getClass(); 409 String pdpActions = pdp.getActions(); 410 String pdpName = pdp.getName(); 411 for (int i = 0; i < plVector.size(); i++) { 412 Permission pp = plVector.get(i); 413 if (pdpClass.isInstance(pp)) { 414 // The equals() method on some permissions 415 // have some side effects so this manual 416 // comparison is sufficient. 417 if (pdpName.equals(pp.getName()) && 418 pdpActions.equals(pp.getActions())) { 419 plVector.remove(i); 420 break; 421 } 422 } 423 } 424 } 425 } 426 } 427 428 if (perms !=null) { 429 // the order of adding to merged perms and permissions 430 // needs to preserve the bugfix 4301064 431 432 for (int i = plVector.size()-1; i >= 0; i--) { 433 mergedPerms.add(plVector.get(i)); 434 } 435 } 436 if (permissions != null) { 437 for (int i = pdVector.size()-1; i >= 0; i--) { 438 mergedPerms.add(pdVector.get(i)); 439 } 440 } 441 442 return mergedPerms; 443 } 444 445 /** 446 * Used for storing ProtectionDomains as keys in a Map. 447 */ 448 final class Key {} 449 450 static { 451 SharedSecrets.setJavaSecurityProtectionDomainAccess( 452 new JavaSecurityProtectionDomainAccess() { 453 public ProtectionDomainCache getProtectionDomainCache() { 454 return new ProtectionDomainCache() { 455 private final Map<Key, PermissionCollection> map = 456 Collections.synchronizedMap 457 (new WeakHashMap<Key, PermissionCollection>()); 458 public void put(ProtectionDomain pd, 459 PermissionCollection pc) { 460 map.put((pd == null ? null : pd.key), pc); 461 } 462 public PermissionCollection get(ProtectionDomain pd) { 463 return pd == null ? map.get(null) : map.get(pd.key); 464 } 465 }; 466 } 467 }); 468 } 469 }