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