1. /*
  2. * @(#)PropertyPermission.java 1.24 00/02/02
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.util;
  11. import java.io.Serializable;
  12. import java.io.IOException;
  13. import java.security.*;
  14. /**
  15. * This class is for property permissions.
  16. *
  17. * <P>
  18. * The name is the name of the property ("java.home",
  19. * "os.name", etc). The naming
  20. * convention follows the hierarchical property naming convention.
  21. * Also, an asterisk
  22. * may appear at the end of the name, following a ".", or by itself, to
  23. * signify a wildcard match. For example: "java.*" or "*" is valid,
  24. * "*java" or "a*b" is not valid.
  25. * <P>
  26. * <P>
  27. * The actions to be granted are passed to the constructor in a string containing
  28. * a list of zero or more comma-separated keywords. The possible keywords are
  29. * "read" and "write". Their meaning is defined as follows:
  30. * <P>
  31. * <DL>
  32. * <DT> read
  33. * <DD> read permission. Allows <code>System.getProperty</code> to
  34. * be called.
  35. * <DT> write
  36. * <DD> write permission. Allows <code>System.setProperty</code> to
  37. * be called.
  38. * </DL>
  39. * <P>
  40. * The actions string is converted to lowercase before processing.
  41. * <P>
  42. * Care should be taken before granting code permission to access
  43. * certain system properties. For example, granting permission to
  44. * access the "java.home" system property gives potentially malevolent
  45. * code sensitive information about the system environment (the Java
  46. * installation directory). Also, granting permission to access
  47. * the "user.name" and "user.home" system properties gives potentially
  48. * malevolent code sensitive information about the user environment
  49. * (the user's account name and home directory).
  50. *
  51. * @see java.security.BasicPermission
  52. * @see java.security.Permission
  53. * @see java.security.Permissions
  54. * @see java.security.PermissionCollection
  55. * @see java.lang.SecurityManager
  56. *
  57. * @version 1.24 00/02/02
  58. *
  59. * @author Roland Schemers
  60. * @since 1.2
  61. *
  62. * @serial exclude
  63. */
  64. public final class PropertyPermission extends BasicPermission {
  65. /**
  66. * Read action.
  67. */
  68. private final static int READ = 0x1;
  69. /**
  70. * Write action.
  71. */
  72. private final static int WRITE = 0x2;
  73. /**
  74. * All actions (read,write);
  75. */
  76. private final static int ALL = READ|WRITE;
  77. /**
  78. * No actions.
  79. */
  80. private final static int NONE = 0x0;
  81. /**
  82. * The actions mask.
  83. *
  84. */
  85. private transient int mask;
  86. /**
  87. * The actions string.
  88. *
  89. * @serial
  90. */
  91. private String actions; // Left null as long as possible, then
  92. // created and re-used in the getAction function.
  93. /**
  94. * initialize a PropertyPermission object. Common to all constructors.
  95. * Also called during de-serialization.
  96. *
  97. * @param mask the actions mask to use.
  98. *
  99. */
  100. private void init(int mask)
  101. {
  102. if ((mask & ALL) != mask)
  103. throw new IllegalArgumentException("invalid actions mask");
  104. if (mask == NONE)
  105. throw new IllegalArgumentException("invalid actions mask");
  106. if (getName() == null)
  107. throw new NullPointerException("name can't be null");
  108. this.mask = mask;
  109. }
  110. /**
  111. * Creates a new PropertyPermission object with the specified name.
  112. * The name is the name of the system property, and
  113. * <i>actions</i> contains a comma-separated list of the
  114. * desired actions granted on the property. Possible actions are
  115. * "read" and "write".
  116. *
  117. * @param name the name of the PropertyPermission.
  118. * @param actions the actions string.
  119. */
  120. public PropertyPermission(String name, String actions)
  121. {
  122. super(name,actions);
  123. init(getMask(actions));
  124. }
  125. /**
  126. * Checks if this PropertyPermission object "implies" the specified
  127. * permission.
  128. * <P>
  129. * More specifically, this method returns true if:<p>
  130. * <ul>
  131. * <li> <i>p</i> is an instanceof PropertyPermission,<p>
  132. * <li> <i>p</i>'s actions are a proper subset of this
  133. * object's actions, and <p>
  134. * <li> <i>p</i>'s name is implied by this object's
  135. * name. For example, "java.*" implies "java.home".
  136. * </ul>
  137. * @param p the permission to check against.
  138. *
  139. * @return true if the specified permission is implied by this object,
  140. * false if not.
  141. */
  142. public boolean implies(Permission p) {
  143. if (!(p instanceof PropertyPermission))
  144. return false;
  145. PropertyPermission that = (PropertyPermission) p;
  146. // we get the effective mask. i.e., the "and" of this and that.
  147. // They must be equal to that.mask for implies to return true.
  148. return ((this.mask & that.mask) == that.mask) && super.implies(that);
  149. }
  150. /**
  151. * Checks two PropertyPermission objects for equality. Checks that <i>obj</i> is
  152. * a PropertyPermission, and has the same name and actions as this object.
  153. * <P>
  154. * @param obj the object we are testing for equality with this object.
  155. * @return true if obj is a PropertyPermission, and has the same name and
  156. * actions as this PropertyPermission object.
  157. */
  158. public boolean equals(Object obj) {
  159. if (obj == this)
  160. return true;
  161. if (! (obj instanceof PropertyPermission))
  162. return false;
  163. PropertyPermission that = (PropertyPermission) obj;
  164. return (this.mask == that.mask) &&
  165. (this.getName().equals(that.getName()));
  166. }
  167. /**
  168. * Returns the hash code value for this object.
  169. * The hash code used is the hash code of this permissions name, that is,
  170. * <code>getName().hashCode()</code>, where <code>getName</code> is
  171. * from the Permission superclass.
  172. *
  173. * @return a hash code value for this object.
  174. */
  175. public int hashCode() {
  176. return this.getName().hashCode();
  177. }
  178. /**
  179. * Converts an actions String to an actions mask.
  180. *
  181. * @param action the action string.
  182. * @return the actions mask.
  183. */
  184. private static int getMask(String actions) {
  185. int mask = NONE;
  186. if (actions == null) {
  187. return mask;
  188. }
  189. char[] a = actions.toCharArray();
  190. int i = a.length - 1;
  191. if (i < 0)
  192. return mask;
  193. while (i != -1) {
  194. char c;
  195. // skip whitespace
  196. while ((i!=-1) && ((c = a[i]) == ' ' ||
  197. c == '\r' ||
  198. c == '\n' ||
  199. c == '\f' ||
  200. c == '\t'))
  201. i--;
  202. // check for the known strings
  203. int matchlen;
  204. if (i >= 3 && (a[i-3] == 'r' || a[i-3] == 'R') &&
  205. (a[i-2] == 'e' || a[i-2] == 'E') &&
  206. (a[i-1] == 'a' || a[i-1] == 'A') &&
  207. (a[i] == 'd' || a[i] == 'D'))
  208. {
  209. matchlen = 4;
  210. mask |= READ;
  211. } else if (i >= 4 && (a[i-4] == 'w' || a[i-4] == 'W') &&
  212. (a[i-3] == 'r' || a[i-3] == 'R') &&
  213. (a[i-2] == 'i' || a[i-2] == 'I') &&
  214. (a[i-1] == 't' || a[i-1] == 'T') &&
  215. (a[i] == 'e' || a[i] == 'E'))
  216. {
  217. matchlen = 5;
  218. mask |= WRITE;
  219. } else {
  220. // parse error
  221. throw new IllegalArgumentException(
  222. "invalid permission: " + actions);
  223. }
  224. // make sure we didn't just match the tail of a word
  225. // like "ackbarfaccept". Also, skip to the comma.
  226. boolean seencomma = false;
  227. while (i >= matchlen && !seencomma) {
  228. switch(a[i-matchlen]) {
  229. case ',':
  230. seencomma = true;
  231. /*FALLTHROUGH*/
  232. case ' ': case '\r': case '\n':
  233. case '\f': case '\t':
  234. break;
  235. default:
  236. throw new IllegalArgumentException(
  237. "invalid permission: " + actions);
  238. }
  239. i--;
  240. }
  241. // point i at the location of the comma minus one (or -1).
  242. i -= matchlen;
  243. }
  244. return mask;
  245. }
  246. /**
  247. * Return the canonical string representation of the actions.
  248. * Always returns present actions in the following order:
  249. * read, write.
  250. *
  251. * @return the canonical string representation of the actions.
  252. */
  253. static String getActions(int mask)
  254. {
  255. StringBuffer sb = new StringBuffer();
  256. boolean comma = false;
  257. if ((mask & READ) == READ) {
  258. comma = true;
  259. sb.append("read");
  260. }
  261. if ((mask & WRITE) == WRITE) {
  262. if (comma) sb.append(',');
  263. else comma = true;
  264. sb.append("write");
  265. }
  266. return sb.toString();
  267. }
  268. /**
  269. * Returns the "canonical string representation" of the actions.
  270. * That is, this method always returns present actions in the following order:
  271. * read, write. For example, if this PropertyPermission object
  272. * allows both write and read actions, a call to <code>getActions</code>
  273. * will return the string "read,write".
  274. *
  275. * @return the canonical string representation of the actions.
  276. */
  277. public String getActions()
  278. {
  279. if (actions == null)
  280. actions = getActions(this.mask);
  281. return actions;
  282. }
  283. /**
  284. * Return the current action mask.
  285. * Used by the PropertyPermissionCollection
  286. *
  287. * @return the actions mask.
  288. */
  289. int getMask() {
  290. return mask;
  291. }
  292. /**
  293. * Returns a new PermissionCollection object for storing
  294. * PropertyPermission objects.
  295. * <p>
  296. *
  297. * @return a new PermissionCollection object suitable for storing
  298. * PropertyPermissions.
  299. */
  300. public PermissionCollection newPermissionCollection() {
  301. return new PropertyPermissionCollection();
  302. }
  303. /**
  304. * WriteObject is called to save the state of the PropertyPermission
  305. * to a stream. The actions are serialized, and the superclass
  306. * takes care of the name.
  307. */
  308. private synchronized void writeObject(java.io.ObjectOutputStream s)
  309. throws IOException
  310. {
  311. // Write out the actions. The superclass takes care of the name
  312. // call getActions to make sure actions field is initialized
  313. if (actions == null)
  314. getActions();
  315. s.defaultWriteObject();
  316. }
  317. /**
  318. * readObject is called to restore the state of the PropertyPermission from
  319. * a stream.
  320. */
  321. private synchronized void readObject(java.io.ObjectInputStream s)
  322. throws IOException, ClassNotFoundException
  323. {
  324. // Read in the action, then initialize the rest
  325. s.defaultReadObject();
  326. init(getMask(actions));
  327. }
  328. }
  329. /**
  330. * A PropertyPermissionCollection stores a set of PropertyPermission
  331. * permissions.
  332. *
  333. * @see java.security.Permission
  334. * @see java.security.Permissions
  335. * @see java.security.PermissionCollection
  336. *
  337. * @version 1.24, 02/02/00
  338. *
  339. * @author Roland Schemers
  340. *
  341. * @serial include
  342. */
  343. final class PropertyPermissionCollection extends PermissionCollection
  344. implements Serializable
  345. {
  346. /**
  347. * Table of permissions.
  348. *
  349. * @serial
  350. */
  351. private Hashtable permissions;
  352. /**
  353. * Boolean saying if "*" is in the collection.
  354. *
  355. * @serial
  356. */
  357. private boolean all_allowed;
  358. /**
  359. * Create an empty PropertyPermissions object.
  360. *
  361. */
  362. public PropertyPermissionCollection() {
  363. permissions = new Hashtable();
  364. all_allowed = false;
  365. }
  366. /**
  367. * Adds a permission to the PropertyPermissions. The key for the hash is
  368. * the name.
  369. *
  370. * @param permission the Permission object to add.
  371. *
  372. * @exception IllegalArgumentException - if the permission is not a
  373. * PropertyPermission
  374. *
  375. * @exception SecurityException - if this PropertyPermissionCollection
  376. * object has been marked readonly
  377. */
  378. public void add(Permission permission)
  379. {
  380. if (! (permission instanceof PropertyPermission))
  381. throw new IllegalArgumentException("invalid permission: "+
  382. permission);
  383. if (isReadOnly())
  384. throw new SecurityException("attempt to add a Permission to a readonly PermissionCollection");
  385. PropertyPermission pp = (PropertyPermission) permission;
  386. PropertyPermission existing =
  387. (PropertyPermission) permissions.get(pp.getName());
  388. if (existing != null) {
  389. int oldMask = existing.getMask();
  390. int newMask = pp.getMask();
  391. if (oldMask != newMask) {
  392. int effective = oldMask | newMask;
  393. String actions = PropertyPermission.getActions(effective);
  394. permissions.put(pp.getName(),
  395. new PropertyPermission(pp.getName(), actions));
  396. }
  397. } else {
  398. permissions.put(pp.getName(), permission);
  399. }
  400. if (!all_allowed) {
  401. if (pp.getName().equals("*"))
  402. all_allowed = true;
  403. }
  404. }
  405. /**
  406. * Check and see if this set of permissions implies the permissions
  407. * expressed in "permission".
  408. *
  409. * @param p the Permission object to compare
  410. *
  411. * @return true if "permission" is a proper subset of a permission in
  412. * the set, false if not.
  413. */
  414. public boolean implies(Permission permission)
  415. {
  416. if (! (permission instanceof PropertyPermission))
  417. return false;
  418. PropertyPermission pp = (PropertyPermission) permission;
  419. PropertyPermission x;
  420. int desired = pp.getMask();
  421. int effective = 0;
  422. // short circuit if the "*" Permission was added
  423. if (all_allowed) {
  424. x = (PropertyPermission) permissions.get("*");
  425. if (x != null) {
  426. effective |= x.getMask();
  427. if ((effective & desired) == desired)
  428. return true;
  429. }
  430. }
  431. // strategy:
  432. // Check for full match first. Then work our way up the
  433. // name looking for matches on a.b.*
  434. String name = pp.getName();
  435. //System.out.println("check "+name);
  436. x = (PropertyPermission) permissions.get(name);
  437. if (x != null) {
  438. // we have a direct hit!
  439. effective |= x.getMask();
  440. if ((effective & desired) == desired)
  441. return true;
  442. }
  443. // work our way up the tree...
  444. int last, offset;
  445. offset = name.length()-1;
  446. while ((last = name.lastIndexOf(".", offset)) != -1) {
  447. name = name.substring(0, last+1) + "*";
  448. //System.out.println("check "+name);
  449. x = (PropertyPermission) permissions.get(name);
  450. if (x != null) {
  451. effective |= x.getMask();
  452. if ((effective & desired) == desired)
  453. return true;
  454. }
  455. offset = last -1;
  456. }
  457. // we don't have to check for "*" as it was already checked
  458. // at the top (all_allowed), so we just return false
  459. return false;
  460. }
  461. /**
  462. * Returns an enumeration of all the PropertyPermission objects in the
  463. * container.
  464. *
  465. * @return an enumeration of all the PropertyPermission objects.
  466. */
  467. public Enumeration elements()
  468. {
  469. return permissions.elements();
  470. }
  471. }