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