1. /*
  2. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  3. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  4. */
  5. package javax.security.auth;
  6. import java.security.AccessController;
  7. import java.security.AccessControlContext;
  8. import java.security.AllPermission;
  9. import java.security.Permission;
  10. import java.security.Permissions;
  11. import java.security.PermissionCollection;
  12. import java.security.PrivilegedAction;
  13. import java.security.ProtectionDomain;
  14. import java.util.Hashtable;
  15. /**
  16. * A <code>SubjectDomainCombiner</code> updates the
  17. * ProtectionDomains affiliated with an <code>AccessControlContext</code>
  18. * with the relevant Subject-based Permissions configured in the
  19. * <code>java.security.auth.Policy</code>.
  20. *
  21. * <p> If the <code>Security</code> property, <i>cache.auth.policy</i>,
  22. * either is not set, or if it is set to <i>true</i>, then this
  23. * <code>SubjectDomainCombiner</code> implementation caches
  24. * policy information. Otherwise, this <code>SubjectDomainCombiner</code>
  25. * implementation refrains from performing any caching.
  26. * The <i>cache.auth.policy</i> property may be set in the Security
  27. * properties file, located in the file named
  28. * <JAVA_HOME>/lib/security/java.security, where <JAVA_HOME>
  29. * refers to the directory where the SDK was installed.
  30. *
  31. * @version 1.19, 01/13/00
  32. */
  33. public class SubjectDomainCombiner implements java.security.DomainCombiner {
  34. private static Policy policy;
  35. private Subject subject;
  36. private Hashtable cachedProtectionDomains = new Hashtable();
  37. private static boolean checkedCacheProperty = false;
  38. private static boolean allowCaching = true;
  39. private static final AllPermission ALL_PERMISSION = new AllPermission();
  40. private static final Debug debug = Debug.getInstance("combiner",
  41. "\t[SubjectDomainCombiner]");
  42. /**
  43. * Associate the provided <code>Subject</code> with this
  44. * <code>SubjectDomainCombiner</code>.
  45. *
  46. * <p>
  47. *
  48. * @param subject the <code>Subject</code> to be associated with
  49. * with this <code>SubjectDomainCombiner</code>.
  50. */
  51. public SubjectDomainCombiner(Subject subject) {
  52. this.subject = subject;
  53. // XXX must go here -- that way the policy gets initialized
  54. // before anyone has done a doPrivileged or doAs
  55. // with a SubjectDomainCombiner. otherwise,
  56. // you will get into an infinite loop since:
  57. // 1) combine calls Policy.getPolicy()
  58. // 2) a security check is invoked
  59. // 3) SecurityManager calls AccessController.getContext()
  60. // 4) combine() gets called again
  61. if (policy == null)
  62. policy = Policy.getPolicy();
  63. // see if we allow caching of the permissions
  64. if (!checkedCacheProperty) {
  65. allowCaching = cachePolicy();
  66. checkedCacheProperty = true;
  67. }
  68. }
  69. /**
  70. * Get the <code>Subject</code> associated with this
  71. * <code>SubjectDomainCombiner</code>.
  72. *
  73. * <p>
  74. *
  75. * @return the <code>Subject</code> associated with this
  76. * <code>SubjectDomainCombiner</code>, or <code>null</code>
  77. * if no <code>Subject</code> is associated with this
  78. * <code>SubjectDomainCombiner</code>.
  79. *
  80. * @exception SecurityException if the caller does not have permission
  81. * to get the <code>Subject</code> associated with this
  82. * <code>SubjectDomainCombiner</code>.
  83. */
  84. public Subject getSubject() {
  85. java.lang.SecurityManager sm = System.getSecurityManager();
  86. if (sm != null) {
  87. sm.checkPermission(new AuthPermission
  88. ("getSubjectFromDomainCombiner"));
  89. }
  90. return subject;
  91. }
  92. /**
  93. * Update the provided ProtectionDomains with the relevant
  94. * Subject-based Permissions.
  95. *
  96. * <p> For each <code>ProtectionDomain</code> in the
  97. * <i>currentDomains</i> array, this method retrieves the
  98. * Subject-based permissions granted in the <code>Policy</code>.
  99. * To retrieve these permissions, this method invokes the
  100. * <code>getPermissions</code> method from the <code>Policy</code>,
  101. * passing it the <code>Subject</code> associated with this
  102. * <code>SubjectDomainCombiner</code>, along with the
  103. * <code>CodeSource</code> associated with the respective
  104. * <code>ProtectionDomain</code>.
  105. * A new collection of Permissions is created from the union of the
  106. * retrieved Permissions and the original Permissions granted to that
  107. * <code>ProtectionDomain</code>. A new
  108. * <code>ProtectionDomain</code> is then instantiated
  109. * with this new collection of Permissions, as well as the
  110. * <code>CodeSource</code> from the original <code>ProtectionDomain</code>.
  111. * All of the newly instantiated ProtectionDomains are then
  112. * combined into a new array. The ProtectionDomains from the
  113. * <i>assignedDomains</i> array are appended to this new array,
  114. * and the result is returned.
  115. *
  116. * <p> Note that optimizations such as the removal of duplicate entries
  117. * in the array, or the removal of duplicate Permissions in a
  118. * <code>ProtectionDomain</code> entry may have occurred.
  119. * Also note that if caching is permitted and the relevant
  120. * Permissions were already cached, this method does not need to consult
  121. * the <code>Policy</code> to retrieve the Subject-based permissions.
  122. * It simply accesses the cached permissions.
  123. *
  124. * <p>
  125. *
  126. * @param currentDomains the ProtectionDomains associated with the
  127. * current execution Thread, up to the most recent
  128. * privileged <code>ProtectionDomain</code>.
  129. * The ProtectionDomains are are listed in order of execution,
  130. * with the most recently executing <code>ProtectionDomain</code>
  131. * residing at the beginning of the array. This parameter may
  132. * be <code>null</code> if the current execution Thread
  133. * has no associated ProtectionDomains.<p>
  134. *
  135. * @param assignedDomains the ProtectionDomains inherited from the
  136. * parent Thread, or the ProtectionDomains from the
  137. * privileged <i>context</i>, if a call to
  138. * AccessController.doPrivileged(..., <i>context</i>)
  139. * had occurred This parameter may be <code>null</code>
  140. * if there were no ProtectionDomains inherited from the
  141. * parent Thread, or from the privileged <i>context</i>.
  142. *
  143. * @return a new array consisting of the updated ProtectionDomains,
  144. * or <code>null</code>.
  145. */
  146. public ProtectionDomain[] combine(ProtectionDomain[] currentDomains,
  147. ProtectionDomain[] assignedDomains) {
  148. if (currentDomains == null || currentDomains.length == 0)
  149. return optimize(assignedDomains);
  150. if (debug != null) {
  151. if (subject == null) {
  152. debug.println("null subject");
  153. } else {
  154. final Subject s = subject;
  155. AccessController.doPrivileged
  156. (new java.security.PrivilegedAction() {
  157. public Object run() {
  158. debug.println(s.toString());
  159. return null;
  160. }
  161. });
  162. }
  163. printInputDomains(currentDomains, assignedDomains);
  164. }
  165. if (!allowCaching) {
  166. java.security.AccessController.doPrivileged
  167. (new PrivilegedAction() {
  168. public Object run() {
  169. policy.refresh();
  170. return null;
  171. }
  172. });
  173. }
  174. // optimize the inputs
  175. currentDomains = optimize(currentDomains);
  176. assignedDomains = optimize(assignedDomains);
  177. if (debug != null) {
  178. debug.println("after optimize");
  179. printInputDomains(currentDomains, assignedDomains);
  180. }
  181. if (currentDomains == null && assignedDomains == null)
  182. return null;
  183. int cLen = (currentDomains == null ? 0 : currentDomains.length);
  184. int aLen = (assignedDomains == null ? 0 : assignedDomains.length);
  185. // the ProtectionDomains for the new AccessControlContext
  186. // that we will return
  187. ProtectionDomain[] newDomains = new ProtectionDomain[cLen + aLen];
  188. int newDomainIndex = 0;
  189. // update the permissions for the currentDomains
  190. for (int i = 0; i < cLen; i++) {
  191. // XXX
  192. // one of the negatives of moving the PolicyFile implementation
  193. // to the com.sun package is that we no longer have a
  194. // SubjectCodeSource implementation to use here
  195. // (for debugging or as a hash index for caching).
  196. // we could duplicate SubjectCodeSource, but that's not good either.
  197. java.security.CodeSource cs = currentDomains[i].getCodeSource();
  198. ProtectionDomain subjectPd = null;
  199. CacheEntry ce = new CacheEntry(subject, cs);
  200. if ((subjectPd = (ProtectionDomain)
  201. cachedProtectionDomains.get(ce)) == null) {
  202. // XXX
  203. // we must first add the original permissions.
  204. // that way when we later add the new JAAS permissions,
  205. // any unresolved JAAS-related permissions will
  206. // automatically get resolved.
  207. // get the original perms
  208. Permissions perms = new Permissions();
  209. java.util.Enumeration e =
  210. currentDomains[i].getPermissions().elements();
  211. while (e.hasMoreElements()) {
  212. Permission newPerm = (Permission)e.nextElement();
  213. perms.add(newPerm);
  214. }
  215. // get perms from the policy
  216. PermissionCollection newPerms = null;
  217. final java.security.CodeSource finalCs = cs;
  218. final Subject finalS = subject;
  219. newPerms = (PermissionCollection)
  220. java.security.AccessController.doPrivileged
  221. (new PrivilegedAction() {
  222. public Object run() {
  223. return policy.getPermissions(finalS, finalCs);
  224. }
  225. });
  226. // add the newly granted perms,
  227. // avoiding duplicates
  228. e = newPerms.elements();
  229. while (e.hasMoreElements()) {
  230. Permission newPerm = (Permission)e.nextElement();
  231. if (!perms.implies(newPerm))
  232. perms.add(newPerm);
  233. }
  234. subjectPd = new ProtectionDomain(cs, perms);
  235. }
  236. newDomains[newDomainIndex++] = subjectPd;
  237. if (allowCaching)
  238. cachedProtectionDomains.put(ce, subjectPd);
  239. }
  240. if (debug != null) {
  241. debug.println("updated current: ");
  242. if (newDomainIndex == 0) {
  243. debug.println("\tkept nothing");
  244. } else {
  245. for (int j = 0; j < newDomainIndex; j++) {
  246. debug.println("\tupdated[" + j + "] = " + newDomains[j]);
  247. }
  248. }
  249. }
  250. // now add on the assigned domains
  251. if (aLen > 0) {
  252. System.arraycopy(assignedDomains, 0,
  253. newDomains, newDomainIndex,
  254. aLen);
  255. }
  256. // optimize the result
  257. newDomains = optimize(newDomains);
  258. if (debug != null) {
  259. if (newDomains == null || newDomains.length == 0) {
  260. debug.println("returning null");
  261. } else {
  262. debug.println("combinedDomains: ");
  263. for (int i = 0; i < newDomains.length; i++) {
  264. debug.println("newDomain " + i + ": " +
  265. newDomains[i].toString());
  266. }
  267. }
  268. }
  269. // return the new ProtectionDomains
  270. if (newDomains == null || newDomains.length == 0) {
  271. return null;
  272. } else {
  273. return newDomains;
  274. }
  275. }
  276. ProtectionDomain[] optimize(ProtectionDomain[] domains) {
  277. if (domains == null)
  278. return null;
  279. ProtectionDomain[] optimized = new ProtectionDomain[domains.length];
  280. int num = 0;
  281. for (int i = 0; i < domains.length; i++) {
  282. // skip System Domains
  283. if (domains[i] == null)
  284. continue;
  285. // skip domains with AllPermission
  286. // XXX
  287. //
  288. // if (domains[i].implies(ALL_PERMISSION))
  289. // continue;
  290. // remove duplicates
  291. boolean foundIt = false;
  292. for (int j = 0; j < num; j++) {
  293. if (optimized[j] == domains[i]) {
  294. foundIt = true;
  295. break;
  296. }
  297. }
  298. if (foundIt == false)
  299. optimized[num++] = domains[i];
  300. }
  301. // resize the array if necessary
  302. if (num < domains.length) {
  303. ProtectionDomain[] downSize = new ProtectionDomain[num];
  304. System.arraycopy(optimized, 0, downSize, 0, downSize.length);
  305. optimized = downSize;
  306. }
  307. return (optimized.length == 0 ? null : optimized);
  308. }
  309. private boolean cachePolicy() {
  310. String s = (String)AccessController.doPrivileged
  311. (new PrivilegedAction() {
  312. public Object run() {
  313. return java.security.Security.getProperty
  314. ("cache.auth.policy");
  315. }
  316. });
  317. if (s != null) {
  318. Boolean b = new Boolean(s);
  319. return b.booleanValue();
  320. }
  321. // cache by default
  322. return true;
  323. }
  324. private void printInputDomains(ProtectionDomain[] currentDomains,
  325. ProtectionDomain[] assignedDomains) {
  326. if (currentDomains == null || currentDomains.length == 0) {
  327. debug.println("currentDomains null or 0 length");
  328. } else {
  329. for (int i = 0; currentDomains != null &&
  330. i < currentDomains.length; i++) {
  331. if (currentDomains[i] == null) {
  332. debug.println("currentDomain " + i + ": SystemDomain");
  333. } else {
  334. debug.println("currentDomain " + i + ": " +
  335. currentDomains[i].toString());
  336. }
  337. }
  338. }
  339. if (assignedDomains == null || assignedDomains.length == 0) {
  340. debug.println("assignedDomains null or 0 length");
  341. } else {
  342. debug.println("assignedDomains = ");
  343. for (int i = 0; assignedDomains != null &&
  344. i < assignedDomains.length; i++) {
  345. if (assignedDomains[i] == null) {
  346. debug.println("assignedDomain " + i + ": SystemDomain");
  347. } else {
  348. debug.println("assignedDomain " + i + ": " +
  349. assignedDomains[i].toString());
  350. }
  351. }
  352. }
  353. }
  354. /**
  355. * It would be nice to use a SubjectCodeSource instead of this class.
  356. * but since SubjectCodeSource is package private inside of
  357. * the com package, and we do not want to duplicate it in this package,
  358. * we will live with having this inner class.
  359. */
  360. private class CacheEntry {
  361. private Subject subject;
  362. private java.security.CodeSource codesource;
  363. public CacheEntry(Subject s, java.security.CodeSource cs) {
  364. this.subject = s;
  365. this.codesource = cs;
  366. }
  367. public boolean equals(Object o) {
  368. if (o == null)
  369. return false;
  370. if (this == o)
  371. return true;
  372. if (o instanceof CacheEntry) {
  373. CacheEntry that = (CacheEntry)o;
  374. // compare codesources
  375. if ((this.codesource == null && that.codesource == null) ||
  376. (this.codesource != null && that.codesource != null &&
  377. this.codesource.equals(that.codesource))) {
  378. // compare subject principals
  379. if ((this.subject == null && that.subject == null) ||
  380. (this.subject != null && that.subject != null &&
  381. this.subject.getPrincipals().containsAll
  382. (that.subject.getPrincipals()) &&
  383. that.subject.getPrincipals().containsAll
  384. (this.subject.getPrincipals()))) {
  385. return true;
  386. }
  387. }
  388. }
  389. return false;
  390. }
  391. public int hashCode() {
  392. if (codesource != null) {
  393. return codesource.hashCode();
  394. } else {
  395. if (subject == null)
  396. return 0;
  397. else
  398. return subject.hashCode();
  399. }
  400. }
  401. }
  402. }