1. /*
  2. * @(#)SubjectDomainCombiner.java 1.45 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.security.auth;
  8. import java.security.AccessController;
  9. import java.security.AccessControlContext;
  10. import java.security.AllPermission;
  11. import java.security.Permission;
  12. import java.security.Permissions;
  13. import java.security.PermissionCollection;
  14. import java.security.Policy;
  15. import java.security.Principal;
  16. import java.security.PrivilegedAction;
  17. import java.security.ProtectionDomain;
  18. import java.lang.ClassLoader;
  19. import java.security.Security;
  20. import java.util.Set;
  21. import java.util.Iterator;
  22. /**
  23. * A <code>SubjectDomainCombiner</code> updates ProtectionDomains
  24. * with Principals from the <code>Subject</code> associated with this
  25. * <code>SubjectDomainCombiner</code>.
  26. *
  27. * @version 1.45, 12/19/03
  28. */
  29. public class SubjectDomainCombiner implements java.security.DomainCombiner {
  30. private Subject subject;
  31. private java.util.Map cachedPDs = new java.util.WeakHashMap();
  32. private Set principalSet;
  33. private Principal[] principals;
  34. private static final sun.security.util.Debug debug =
  35. sun.security.util.Debug.getInstance("combiner",
  36. "\t[SubjectDomainCombiner]");
  37. // Note: check only at classloading time, not dynamically during combine()
  38. private static final boolean useJavaxPolicy = compatPolicy();
  39. // Relevant only when useJavaxPolicy is true
  40. private static final boolean allowCaching =
  41. (useJavaxPolicy && cachePolicy());
  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. if (subject.isReadOnly()) {
  54. principalSet = subject.getPrincipals();
  55. principals = (Principal[])principalSet.toArray
  56. (new Principal[principalSet.size()]);
  57. }
  58. }
  59. /**
  60. * Get the <code>Subject</code> associated with this
  61. * <code>SubjectDomainCombiner</code>.
  62. *
  63. * <p>
  64. *
  65. * @return the <code>Subject</code> associated with this
  66. * <code>SubjectDomainCombiner</code>, or <code>null</code>
  67. * if no <code>Subject</code> is associated with this
  68. * <code>SubjectDomainCombiner</code>.
  69. *
  70. * @exception SecurityException if the caller does not have permission
  71. * to get the <code>Subject</code> associated with this
  72. * <code>SubjectDomainCombiner</code>.
  73. */
  74. public Subject getSubject() {
  75. java.lang.SecurityManager sm = System.getSecurityManager();
  76. if (sm != null) {
  77. sm.checkPermission(new AuthPermission
  78. ("getSubjectFromDomainCombiner"));
  79. }
  80. return subject;
  81. }
  82. /**
  83. * Update the relevant ProtectionDomains with the Principals
  84. * from the <code>Subject</code> associated with this
  85. * <code>SubjectDomainCombiner</code>.
  86. *
  87. * <p> A new <code>ProtectionDomain</code> instance is created
  88. * for each <code>ProtectionDomain</code> in the
  89. * <i>currentDomains</i> array. Each new <code>ProtectionDomain</code>
  90. * instance is created using the <code>CodeSource</code>,
  91. * <code>Permission</code>s and <code>ClassLoader</code>
  92. * from the corresponding <code>ProtectionDomain</code> in
  93. * <i>currentDomains</i>, as well as with the Principals from
  94. * the <code>Subject</code> associated with this
  95. * <code>SubjectDomainCombiner</code>.
  96. *
  97. * <p> All of the newly instantiated ProtectionDomains are
  98. * combined into a new array. The ProtectionDomains from the
  99. * <i>assignedDomains</i> array are appended to this new array,
  100. * and the result is returned.
  101. *
  102. * <p> Note that optimizations such as the removal of duplicate
  103. * ProtectionDomains may have occurred.
  104. * In addition, caching of ProtectionDomains may be permitted.
  105. *
  106. * <p>
  107. *
  108. * @param currentDomains the ProtectionDomains associated with the
  109. * current execution Thread, up to the most recent
  110. * privileged <code>ProtectionDomain</code>.
  111. * The ProtectionDomains are are listed in order of execution,
  112. * with the most recently executing <code>ProtectionDomain</code>
  113. * residing at the beginning of the array. This parameter may
  114. * be <code>null</code> if the current execution Thread
  115. * has no associated ProtectionDomains.<p>
  116. *
  117. * @param assignedDomains the ProtectionDomains inherited from the
  118. * parent Thread, or the ProtectionDomains from the
  119. * privileged <i>context</i>, if a call to
  120. * AccessController.doPrivileged(..., <i>context</i>)
  121. * had occurred This parameter may be <code>null</code>
  122. * if there were no ProtectionDomains inherited from the
  123. * parent Thread, or from the privileged <i>context</i>.
  124. *
  125. * @return a new array consisting of the updated ProtectionDomains,
  126. * or <code>null</code>.
  127. */
  128. public ProtectionDomain[] combine(ProtectionDomain[] currentDomains,
  129. ProtectionDomain[] assignedDomains) {
  130. if (debug != null) {
  131. if (subject == null) {
  132. debug.println("null subject");
  133. } else {
  134. final Subject s = subject;
  135. AccessController.doPrivileged
  136. (new java.security.PrivilegedAction() {
  137. public Object run() {
  138. debug.println(s.toString());
  139. return null;
  140. }
  141. });
  142. }
  143. printInputDomains(currentDomains, assignedDomains);
  144. }
  145. if (currentDomains == null || currentDomains.length == 0) {
  146. // No need to optimize assignedDomains because it should
  147. // have been previously optimized (when it was set).
  148. // Note that we are returning a direct reference
  149. // to the input array - since ACC does not clone
  150. // the arrays when it calls combiner.combine,
  151. // multiple ACC instances may share the same
  152. // array instance in this case
  153. return assignedDomains;
  154. }
  155. // optimize currentDomains
  156. //
  157. // No need to optimize assignedDomains because it should
  158. // have been previously optimized (when it was set).
  159. currentDomains = optimize(currentDomains);
  160. if (debug != null) {
  161. debug.println("after optimize");
  162. printInputDomains(currentDomains, assignedDomains);
  163. }
  164. if (currentDomains == null && assignedDomains == null) {
  165. return null;
  166. }
  167. // maintain backwards compatibility for people who provide
  168. // their own javax.security.auth.Policy implementations
  169. if (useJavaxPolicy) {
  170. return combineJavaxPolicy(currentDomains, assignedDomains);
  171. }
  172. int cLen = (currentDomains == null ? 0 : currentDomains.length);
  173. int aLen = (assignedDomains == null ? 0 : assignedDomains.length);
  174. // the ProtectionDomains for the new AccessControlContext
  175. // that we will return
  176. ProtectionDomain[] newDomains = new ProtectionDomain[cLen + aLen];
  177. boolean allNew = true;
  178. synchronized(cachedPDs) {
  179. if (!subject.isReadOnly() &&
  180. !subject.getPrincipals().equals(principalSet)) {
  181. // if the Subject was mutated, clear the PD cache
  182. Set newSet = subject.getPrincipals();
  183. synchronized(newSet) {
  184. principalSet = new java.util.HashSet(newSet);
  185. }
  186. principals = (Principal[])principalSet.toArray
  187. (new Principal[principalSet.size()]);
  188. cachedPDs.clear();
  189. if (debug != null) {
  190. debug.println("Subject mutated - clearing cache");
  191. }
  192. }
  193. ProtectionDomain subjectPd;
  194. for (int i = 0; i < cLen; i++) {
  195. ProtectionDomain pd = currentDomains[i];
  196. subjectPd = (ProtectionDomain) cachedPDs.get(pd);
  197. if (subjectPd == null) {
  198. subjectPd = new ProtectionDomain(pd.getCodeSource(),
  199. pd.getPermissions(),
  200. pd.getClassLoader(),
  201. principals);
  202. cachedPDs.put(pd, subjectPd);
  203. } else {
  204. allNew = false;
  205. }
  206. newDomains[i] = subjectPd;
  207. }
  208. }
  209. if (debug != null) {
  210. debug.println("updated current: ");
  211. for (int i = 0; i < cLen; i++) {
  212. debug.println("\tupdated[" + i + "] = " +
  213. printDomain(newDomains[i]));
  214. }
  215. }
  216. // now add on the assigned domains
  217. if (aLen > 0) {
  218. System.arraycopy(assignedDomains, 0, newDomains, cLen, aLen);
  219. // optimize the result (cached PDs might exist in assignedDomains)
  220. if (!allNew) {
  221. newDomains = optimize(newDomains);
  222. }
  223. }
  224. // if aLen == 0 || allNew, no need to further optimize newDomains
  225. if (debug != null) {
  226. if (newDomains == null || newDomains.length == 0) {
  227. debug.println("returning null");
  228. } else {
  229. debug.println("combinedDomains: ");
  230. for (int i = 0; i < newDomains.length; i++) {
  231. debug.println("newDomain " + i + ": " +
  232. printDomain(newDomains[i]));
  233. }
  234. }
  235. }
  236. // return the new ProtectionDomains
  237. if (newDomains == null || newDomains.length == 0) {
  238. return null;
  239. } else {
  240. return newDomains;
  241. }
  242. }
  243. /**
  244. * Use the javax.security.auth.Policy implementation
  245. */
  246. private ProtectionDomain[] combineJavaxPolicy(
  247. ProtectionDomain[] currentDomains,
  248. ProtectionDomain[] assignedDomains) {
  249. if (!allowCaching) {
  250. java.security.AccessController.doPrivileged
  251. (new PrivilegedAction() {
  252. public Object run() {
  253. // Call refresh only caching is disallowed
  254. javax.security.auth.Policy.getPolicy().refresh();
  255. return null;
  256. }
  257. });
  258. }
  259. int cLen = (currentDomains == null ? 0 : currentDomains.length);
  260. int aLen = (assignedDomains == null ? 0 : assignedDomains.length);
  261. // the ProtectionDomains for the new AccessControlContext
  262. // that we will return
  263. ProtectionDomain[] newDomains = new ProtectionDomain[cLen + aLen];
  264. synchronized(cachedPDs) {
  265. if (!subject.isReadOnly() &&
  266. !subject.getPrincipals().equals(principalSet)) {
  267. // if the Subject was mutated, clear the PD cache
  268. Set newSet = subject.getPrincipals();
  269. synchronized(newSet) {
  270. principalSet = new java.util.HashSet(newSet);
  271. }
  272. principals = (Principal[])principalSet.toArray
  273. (new Principal[principalSet.size()]);
  274. cachedPDs.clear();
  275. if (debug != null) {
  276. debug.println("Subject mutated - clearing cache");
  277. }
  278. }
  279. for (int i = 0; i < cLen; i++) {
  280. ProtectionDomain pd = currentDomains[i];
  281. ProtectionDomain subjectPd =
  282. (ProtectionDomain)cachedPDs.get(pd);
  283. if (subjectPd == null) {
  284. // XXX
  285. // we must first add the original permissions.
  286. // that way when we later add the new JAAS permissions,
  287. // any unresolved JAAS-related permissions will
  288. // automatically get resolved.
  289. // get the original perms
  290. Permissions perms = new Permissions();
  291. PermissionCollection coll = pd.getPermissions();
  292. java.util.Enumeration e;
  293. synchronized (coll) {
  294. e = coll.elements();
  295. while (e.hasMoreElements()) {
  296. Permission newPerm = (Permission)e.nextElement();
  297. perms.add(newPerm);
  298. }
  299. }
  300. // get perms from the policy
  301. final java.security.CodeSource finalCs = pd.getCodeSource();
  302. final Subject finalS = subject;
  303. PermissionCollection newPerms = (PermissionCollection)
  304. java.security.AccessController.doPrivileged
  305. (new PrivilegedAction() {
  306. public Object run() {
  307. return
  308. javax.security.auth.Policy.getPolicy().getPermissions
  309. (finalS, finalCs);
  310. }
  311. });
  312. // add the newly granted perms,
  313. // avoiding duplicates
  314. synchronized (newPerms) {
  315. e = newPerms.elements();
  316. while (e.hasMoreElements()) {
  317. Permission newPerm = (Permission)e.nextElement();
  318. if (!perms.implies(newPerm)) {
  319. perms.add(newPerm);
  320. if (debug != null)
  321. debug.println (
  322. "Adding perm " + newPerm + "\n");
  323. }
  324. }
  325. }
  326. subjectPd = new ProtectionDomain(finalCs, perms);
  327. if (allowCaching)
  328. cachedPDs.put(pd, subjectPd);
  329. }
  330. newDomains[i] = subjectPd;
  331. }
  332. }
  333. if (debug != null) {
  334. debug.println("updated current: ");
  335. for (int i = 0; i < cLen; i++) {
  336. debug.println("\tupdated[" + i + "] = " + newDomains[i]);
  337. }
  338. }
  339. // now add on the assigned domains
  340. if (aLen > 0) {
  341. System.arraycopy(assignedDomains, 0, newDomains, cLen, aLen);
  342. }
  343. if (debug != null) {
  344. if (newDomains == null || newDomains.length == 0) {
  345. debug.println("returning null");
  346. } else {
  347. debug.println("combinedDomains: ");
  348. for (int i = 0; i < newDomains.length; i++) {
  349. debug.println("newDomain " + i + ": " +
  350. newDomains[i].toString());
  351. }
  352. }
  353. }
  354. // return the new ProtectionDomains
  355. if (newDomains == null || newDomains.length == 0) {
  356. return null;
  357. } else {
  358. return newDomains;
  359. }
  360. }
  361. private static ProtectionDomain[] optimize(ProtectionDomain[] domains) {
  362. if (domains == null || domains.length == 0)
  363. return null;
  364. ProtectionDomain[] optimized = new ProtectionDomain[domains.length];
  365. ProtectionDomain pd;
  366. int num = 0;
  367. for (int i = 0; i < domains.length; i++) {
  368. // skip domains with AllPermission
  369. // XXX
  370. //
  371. // if (domains[i].implies(ALL_PERMISSION))
  372. // continue;
  373. // skip System Domains
  374. if ((pd = domains[i]) != null) {
  375. // remove duplicates
  376. boolean found = false;
  377. for (int j = 0; j < num && !found; j++) {
  378. found = (optimized[j] == pd);
  379. }
  380. if (!found) {
  381. optimized[num++] = pd;
  382. }
  383. }
  384. }
  385. // resize the array if necessary
  386. if (num > 0 && num < domains.length) {
  387. ProtectionDomain[] downSize = new ProtectionDomain[num];
  388. System.arraycopy(optimized, 0, downSize, 0, downSize.length);
  389. optimized = downSize;
  390. }
  391. return ((num == 0 || optimized.length == 0) ? null : optimized);
  392. }
  393. private static boolean cachePolicy() {
  394. String s = (String)AccessController.doPrivileged
  395. (new PrivilegedAction() {
  396. public Object run() {
  397. return java.security.Security.getProperty
  398. ("cache.auth.policy");
  399. }
  400. });
  401. if (s != null) {
  402. Boolean b = new Boolean(s);
  403. return b.booleanValue();
  404. }
  405. // cache by default
  406. return true;
  407. }
  408. // maintain backwards compatibility for people who provide
  409. // their own javax.security.auth.Policy implementations
  410. private static boolean compatPolicy() {
  411. javax.security.auth.Policy javaxPolicy =
  412. (javax.security.auth.Policy)AccessController.doPrivileged
  413. (new PrivilegedAction() {
  414. public Object run() {
  415. return javax.security.auth.Policy.getPolicy();
  416. }
  417. });
  418. if (!(javaxPolicy instanceof com.sun.security.auth.PolicyFile)) {
  419. if (debug != null) {
  420. debug.println("Providing backwards compatibility for " +
  421. "javax.security.auth.policy implementation: " +
  422. javaxPolicy.toString());
  423. }
  424. return true;
  425. } else {
  426. return false;
  427. }
  428. }
  429. private static void printInputDomains(ProtectionDomain[] currentDomains,
  430. ProtectionDomain[] assignedDomains) {
  431. if (currentDomains == null || currentDomains.length == 0) {
  432. debug.println("currentDomains null or 0 length");
  433. } else {
  434. for (int i = 0; currentDomains != null &&
  435. i < currentDomains.length; i++) {
  436. if (currentDomains[i] == null) {
  437. debug.println("currentDomain " + i + ": SystemDomain");
  438. } else {
  439. debug.println("currentDomain " + i + ": " +
  440. printDomain(currentDomains[i]));
  441. }
  442. }
  443. }
  444. if (assignedDomains == null || assignedDomains.length == 0) {
  445. debug.println("assignedDomains null or 0 length");
  446. } else {
  447. debug.println("assignedDomains = ");
  448. for (int i = 0; assignedDomains != null &&
  449. i < assignedDomains.length; i++) {
  450. if (assignedDomains[i] == null) {
  451. debug.println("assignedDomain " + i + ": SystemDomain");
  452. } else {
  453. debug.println("assignedDomain " + i + ": " +
  454. printDomain(assignedDomains[i]));
  455. }
  456. }
  457. }
  458. }
  459. private static String printDomain(final ProtectionDomain pd) {
  460. if (pd == null) {
  461. return "null";
  462. }
  463. return (String)AccessController.doPrivileged(new PrivilegedAction() {
  464. public Object run() {
  465. return pd.toString();
  466. }
  467. });
  468. }
  469. }