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