1/* 2 * Copyright (c) 1997, 2015, 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 26package java.security; 27 28import java.util.Enumeration; 29import java.util.Hashtable; 30import java.util.NoSuchElementException; 31import java.util.Map; 32import java.util.HashMap; 33import java.util.List; 34import java.util.Iterator; 35import java.util.Collections; 36import java.util.concurrent.ConcurrentHashMap; 37import java.io.Serializable; 38import java.io.ObjectStreamField; 39import java.io.ObjectOutputStream; 40import java.io.ObjectInputStream; 41import java.io.IOException; 42 43 44/** 45 * This class represents a heterogeneous collection of Permissions. That is, 46 * it contains different types of Permission objects, organized into 47 * PermissionCollections. For example, if any 48 * {@code java.io.FilePermission} objects are added to an instance of 49 * this class, they are all stored in a single 50 * PermissionCollection. It is the PermissionCollection returned by a call to 51 * the {@code newPermissionCollection} method in the FilePermission class. 52 * Similarly, any {@code java.lang.RuntimePermission} objects are 53 * stored in the PermissionCollection returned by a call to the 54 * {@code newPermissionCollection} method in the 55 * RuntimePermission class. Thus, this class represents a collection of 56 * PermissionCollections. 57 * 58 * <p>When the {@code add} method is called to add a Permission, the 59 * Permission is stored in the appropriate PermissionCollection. If no such 60 * collection exists yet, the Permission object's class is determined and the 61 * {@code newPermissionCollection} method is called on that class to create 62 * the PermissionCollection and add it to the Permissions object. If 63 * {@code newPermissionCollection} returns null, then a default 64 * PermissionCollection that uses a hashtable will be created and used. Each 65 * hashtable entry stores a Permission object as both the key and the value. 66 * 67 * <p> Enumerations returned via the {@code elements} method are 68 * not <em>fail-fast</em>. Modifications to a collection should not be 69 * performed while enumerating over that collection. 70 * 71 * @see Permission 72 * @see PermissionCollection 73 * @see AllPermission 74 * 75 * 76 * @author Marianne Mueller 77 * @author Roland Schemers 78 * @since 1.2 79 * 80 * @serial exclude 81 */ 82 83public final class Permissions extends PermissionCollection 84implements Serializable 85{ 86 /** 87 * Key is permissions Class, value is PermissionCollection for that class. 88 * Not serialized; see serialization section at end of class. 89 */ 90 private transient ConcurrentHashMap<Class<?>, PermissionCollection> permsMap; 91 92 // optimization. keep track of whether unresolved permissions need to be 93 // checked 94 private transient boolean hasUnresolved = false; 95 96 // optimization. keep track of the AllPermission collection 97 // - package private for ProtectionDomain optimization 98 PermissionCollection allPermission; 99 100 /** 101 * Creates a new Permissions object containing no PermissionCollections. 102 */ 103 public Permissions() { 104 permsMap = new ConcurrentHashMap<>(11); 105 allPermission = null; 106 } 107 108 /** 109 * Adds a permission object to the PermissionCollection for the class the 110 * permission belongs to. For example, if <i>permission</i> is a 111 * FilePermission, it is added to the FilePermissionCollection stored 112 * in this Permissions object. 113 * 114 * This method creates 115 * a new PermissionCollection object (and adds the permission to it) 116 * if an appropriate collection does not yet exist. 117 * 118 * @param permission the Permission object to add. 119 * 120 * @exception SecurityException if this Permissions object is 121 * marked as readonly. 122 * 123 * @see PermissionCollection#isReadOnly() 124 */ 125 @Override 126 public void add(Permission permission) { 127 if (isReadOnly()) 128 throw new SecurityException( 129 "attempt to add a Permission to a readonly Permissions object"); 130 131 PermissionCollection pc = getPermissionCollection(permission, true); 132 pc.add(permission); 133 134 // No sync; staleness -> optimizations delayed, which is OK 135 if (permission instanceof AllPermission) { 136 allPermission = pc; 137 } 138 if (permission instanceof UnresolvedPermission) { 139 hasUnresolved = true; 140 } 141 } 142 143 /** 144 * Checks to see if this object's PermissionCollection for permissions of 145 * the specified permission's class implies the permissions 146 * expressed in the <i>permission</i> object. Returns true if the 147 * combination of permissions in the appropriate PermissionCollection 148 * (e.g., a FilePermissionCollection for a FilePermission) together 149 * imply the specified permission. 150 * 151 * <p>For example, suppose there is a FilePermissionCollection in this 152 * Permissions object, and it contains one FilePermission that specifies 153 * "read" access for all files in all subdirectories of the "/tmp" 154 * directory, and another FilePermission that specifies "write" access 155 * for all files in the "/tmp/scratch/foo" directory. 156 * Then if the {@code implies} method 157 * is called with a permission specifying both "read" and "write" access 158 * to files in the "/tmp/scratch/foo" directory, {@code true} is 159 * returned. 160 * 161 * <p>Additionally, if this PermissionCollection contains the 162 * AllPermission, this method will always return true. 163 * 164 * @param permission the Permission object to check. 165 * 166 * @return true if "permission" is implied by the permissions in the 167 * PermissionCollection it 168 * belongs to, false if not. 169 */ 170 @Override 171 public boolean implies(Permission permission) { 172 // No sync; staleness -> skip optimization, which is OK 173 if (allPermission != null) { 174 return true; // AllPermission has already been added 175 } else { 176 PermissionCollection pc = getPermissionCollection(permission, 177 false); 178 if (pc != null) { 179 return pc.implies(permission); 180 } else { 181 // none found 182 return false; 183 } 184 } 185 } 186 187 /** 188 * Returns an enumeration of all the Permission objects in all the 189 * PermissionCollections in this Permissions object. 190 * 191 * @return an enumeration of all the Permissions. 192 */ 193 @Override 194 public Enumeration<Permission> elements() { 195 // go through each Permissions in the hash table 196 // and call their elements() function. 197 198 return new PermissionsEnumerator(permsMap.values().iterator()); 199 } 200 201 /** 202 * Gets the PermissionCollection in this Permissions object for 203 * permissions whose type is the same as that of <i>p</i>. 204 * For example, if <i>p</i> is a FilePermission, 205 * the FilePermissionCollection 206 * stored in this Permissions object will be returned. 207 * 208 * If createEmpty is true, 209 * this method creates a new PermissionCollection object for the specified 210 * type of permission objects if one does not yet exist. 211 * To do so, it first calls the {@code newPermissionCollection} method 212 * on <i>p</i>. Subclasses of class Permission 213 * override that method if they need to store their permissions in a 214 * particular PermissionCollection object in order to provide the 215 * correct semantics when the {@code PermissionCollection.implies} 216 * method is called. 217 * If the call returns a PermissionCollection, that collection is stored 218 * in this Permissions object. If the call returns null and createEmpty 219 * is true, then 220 * this method instantiates and stores a default PermissionCollection 221 * that uses a hashtable to store its permission objects. 222 * 223 * createEmpty is ignored when creating empty PermissionCollection 224 * for unresolved permissions because of the overhead of determining the 225 * PermissionCollection to use. 226 * 227 * createEmpty should be set to false when this method is invoked from 228 * implies() because it incurs the additional overhead of creating and 229 * adding an empty PermissionCollection that will just return false. 230 * It should be set to true when invoked from add(). 231 */ 232 private PermissionCollection getPermissionCollection(Permission p, 233 boolean createEmpty) { 234 Class<?> c = p.getClass(); 235 236 if (!hasUnresolved && !createEmpty) { 237 return permsMap.get(c); 238 } 239 240 // Create and add permission collection to map if it is absent. 241 // NOTE: cannot use lambda for mappingFunction parameter until 242 // JDK-8076596 is fixed. 243 return permsMap.computeIfAbsent(c, 244 new java.util.function.Function<>() { 245 @Override 246 public PermissionCollection apply(Class<?> k) { 247 // Check for unresolved permissions 248 PermissionCollection pc = 249 (hasUnresolved ? getUnresolvedPermissions(p) : null); 250 251 // if still null, create a new collection 252 if (pc == null && createEmpty) { 253 254 pc = p.newPermissionCollection(); 255 256 // still no PermissionCollection? 257 // We'll give them a PermissionsHash. 258 if (pc == null) { 259 pc = new PermissionsHash(); 260 } 261 } 262 return pc; 263 } 264 } 265 ); 266 } 267 268 /** 269 * Resolves any unresolved permissions of type p. 270 * 271 * @param p the type of unresolved permission to resolve 272 * 273 * @return PermissionCollection containing the unresolved permissions, 274 * or null if there were no unresolved permissions of type p. 275 * 276 */ 277 private PermissionCollection getUnresolvedPermissions(Permission p) 278 { 279 UnresolvedPermissionCollection uc = 280 (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class); 281 282 // we have no unresolved permissions if uc is null 283 if (uc == null) 284 return null; 285 286 List<UnresolvedPermission> unresolvedPerms = 287 uc.getUnresolvedPermissions(p); 288 289 // we have no unresolved permissions of this type if unresolvedPerms is null 290 if (unresolvedPerms == null) 291 return null; 292 293 java.security.cert.Certificate[] certs = null; 294 295 Object[] signers = p.getClass().getSigners(); 296 297 int n = 0; 298 if (signers != null) { 299 for (int j=0; j < signers.length; j++) { 300 if (signers[j] instanceof java.security.cert.Certificate) { 301 n++; 302 } 303 } 304 certs = new java.security.cert.Certificate[n]; 305 n = 0; 306 for (int j=0; j < signers.length; j++) { 307 if (signers[j] instanceof java.security.cert.Certificate) { 308 certs[n++] = (java.security.cert.Certificate)signers[j]; 309 } 310 } 311 } 312 313 PermissionCollection pc = null; 314 synchronized (unresolvedPerms) { 315 int len = unresolvedPerms.size(); 316 for (int i = 0; i < len; i++) { 317 UnresolvedPermission up = unresolvedPerms.get(i); 318 Permission perm = up.resolve(p, certs); 319 if (perm != null) { 320 if (pc == null) { 321 pc = p.newPermissionCollection(); 322 if (pc == null) 323 pc = new PermissionsHash(); 324 } 325 pc.add(perm); 326 } 327 } 328 } 329 return pc; 330 } 331 332 private static final long serialVersionUID = 4858622370623524688L; 333 334 // Need to maintain serialization interoperability with earlier releases, 335 // which had the serializable field: 336 // private Hashtable perms; 337 338 /** 339 * @serialField perms java.util.Hashtable 340 * A table of the Permission classes and PermissionCollections. 341 * @serialField allPermission java.security.PermissionCollection 342 */ 343 private static final ObjectStreamField[] serialPersistentFields = { 344 new ObjectStreamField("perms", Hashtable.class), 345 new ObjectStreamField("allPermission", PermissionCollection.class), 346 }; 347 348 /** 349 * @serialData Default fields. 350 */ 351 /* 352 * Writes the contents of the permsMap field out as a Hashtable for 353 * serialization compatibility with earlier releases. allPermission 354 * unchanged. 355 */ 356 private void writeObject(ObjectOutputStream out) throws IOException { 357 // Don't call out.defaultWriteObject() 358 359 // Copy perms into a Hashtable 360 Hashtable<Class<?>, PermissionCollection> perms = 361 new Hashtable<>(permsMap.size()*2); // no sync; estimate 362 perms.putAll(permsMap); 363 364 // Write out serializable fields 365 ObjectOutputStream.PutField pfields = out.putFields(); 366 367 pfields.put("allPermission", allPermission); // no sync; staleness OK 368 pfields.put("perms", perms); 369 out.writeFields(); 370 } 371 372 /* 373 * Reads in a Hashtable of Class/PermissionCollections and saves them in the 374 * permsMap field. Reads in allPermission. 375 */ 376 private void readObject(ObjectInputStream in) throws IOException, 377 ClassNotFoundException { 378 // Don't call defaultReadObject() 379 380 // Read in serialized fields 381 ObjectInputStream.GetField gfields = in.readFields(); 382 383 // Get allPermission 384 allPermission = (PermissionCollection) gfields.get("allPermission", null); 385 386 // Get permissions 387 // writeObject writes a Hashtable<Class<?>, PermissionCollection> for 388 // the perms key, so this cast is safe, unless the data is corrupt. 389 @SuppressWarnings("unchecked") 390 Hashtable<Class<?>, PermissionCollection> perms = 391 (Hashtable<Class<?>, PermissionCollection>)gfields.get("perms", null); 392 permsMap = new ConcurrentHashMap<>(perms.size()*2); 393 permsMap.putAll(perms); 394 395 // Set hasUnresolved 396 UnresolvedPermissionCollection uc = 397 (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class); 398 hasUnresolved = (uc != null && uc.elements().hasMoreElements()); 399 } 400} 401 402final class PermissionsEnumerator implements Enumeration<Permission> { 403 404 // all the perms 405 private Iterator<PermissionCollection> perms; 406 // the current set 407 private Enumeration<Permission> permset; 408 409 PermissionsEnumerator(Iterator<PermissionCollection> e) { 410 perms = e; 411 permset = getNextEnumWithMore(); 412 } 413 414 // No need to synchronize; caller should sync on object as required 415 public boolean hasMoreElements() { 416 // if we enter with permissionimpl null, we know 417 // there are no more left. 418 419 if (permset == null) 420 return false; 421 422 // try to see if there are any left in the current one 423 424 if (permset.hasMoreElements()) 425 return true; 426 427 // get the next one that has something in it... 428 permset = getNextEnumWithMore(); 429 430 // if it is null, we are done! 431 return (permset != null); 432 } 433 434 // No need to synchronize; caller should sync on object as required 435 public Permission nextElement() { 436 437 // hasMoreElements will update permset to the next permset 438 // with something in it... 439 440 if (hasMoreElements()) { 441 return permset.nextElement(); 442 } else { 443 throw new NoSuchElementException("PermissionsEnumerator"); 444 } 445 446 } 447 448 private Enumeration<Permission> getNextEnumWithMore() { 449 while (perms.hasNext()) { 450 PermissionCollection pc = perms.next(); 451 Enumeration<Permission> next =pc.elements(); 452 if (next.hasMoreElements()) 453 return next; 454 } 455 return null; 456 457 } 458} 459 460/** 461 * A PermissionsHash stores a homogeneous set of permissions in a hashtable. 462 * 463 * @see Permission 464 * @see Permissions 465 * 466 * 467 * @author Roland Schemers 468 * 469 * @serial include 470 */ 471 472final class PermissionsHash extends PermissionCollection 473implements Serializable 474{ 475 /** 476 * Key and value are (same) permissions objects. 477 * Not serialized; see serialization section at end of class. 478 */ 479 private transient ConcurrentHashMap<Permission, Permission> permsMap; 480 481 /** 482 * Create an empty PermissionsHash object. 483 */ 484 PermissionsHash() { 485 permsMap = new ConcurrentHashMap<>(11); 486 } 487 488 /** 489 * Adds a permission to the PermissionsHash. 490 * 491 * @param permission the Permission object to add. 492 */ 493 @Override 494 public void add(Permission permission) { 495 permsMap.put(permission, permission); 496 } 497 498 /** 499 * Check and see if this set of permissions implies the permissions 500 * expressed in "permission". 501 * 502 * @param permission the Permission object to compare 503 * 504 * @return true if "permission" is a proper subset of a permission in 505 * the set, false if not. 506 */ 507 @Override 508 public boolean implies(Permission permission) { 509 // attempt a fast lookup and implies. If that fails 510 // then enumerate through all the permissions. 511 Permission p = permsMap.get(permission); 512 513 // If permission is found, then p.equals(permission) 514 if (p == null) { 515 for (Permission p_ : permsMap.values()) { 516 if (p_.implies(permission)) 517 return true; 518 } 519 return false; 520 } else { 521 return true; 522 } 523 } 524 525 /** 526 * Returns an enumeration of all the Permission objects in the container. 527 * 528 * @return an enumeration of all the Permissions. 529 */ 530 @Override 531 public Enumeration<Permission> elements() { 532 return permsMap.elements(); 533 } 534 535 private static final long serialVersionUID = -8491988220802933440L; 536 // Need to maintain serialization interoperability with earlier releases, 537 // which had the serializable field: 538 // private Hashtable perms; 539 /** 540 * @serialField perms java.util.Hashtable 541 * A table of the Permissions (both key and value are same). 542 */ 543 private static final ObjectStreamField[] serialPersistentFields = { 544 new ObjectStreamField("perms", Hashtable.class), 545 }; 546 547 /** 548 * @serialData Default fields. 549 */ 550 /* 551 * Writes the contents of the permsMap field out as a Hashtable for 552 * serialization compatibility with earlier releases. 553 */ 554 private void writeObject(ObjectOutputStream out) throws IOException { 555 // Don't call out.defaultWriteObject() 556 557 // Copy perms into a Hashtable 558 Hashtable<Permission, Permission> perms = 559 new Hashtable<>(permsMap.size()*2); 560 perms.putAll(permsMap); 561 562 // Write out serializable fields 563 ObjectOutputStream.PutField pfields = out.putFields(); 564 pfields.put("perms", perms); 565 out.writeFields(); 566 } 567 568 /* 569 * Reads in a Hashtable of Permission/Permission and saves them in the 570 * permsMap field. 571 */ 572 private void readObject(ObjectInputStream in) throws IOException, 573 ClassNotFoundException { 574 // Don't call defaultReadObject() 575 576 // Read in serialized fields 577 ObjectInputStream.GetField gfields = in.readFields(); 578 579 // Get permissions 580 // writeObject writes a Hashtable<Class<?>, PermissionCollection> for 581 // the perms key, so this cast is safe, unless the data is corrupt. 582 @SuppressWarnings("unchecked") 583 Hashtable<Permission, Permission> perms = 584 (Hashtable<Permission, Permission>)gfields.get("perms", null); 585 permsMap = new ConcurrentHashMap<>(perms.size()*2); 586 permsMap.putAll(perms); 587 } 588} 589