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