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