1. /*
  2. * @(#)AccessControlContext.java 1.40 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 java.security;
  8. import java.util.ArrayList;
  9. import java.util.List;
  10. import sun.security.util.Debug;
  11. import sun.security.util.SecurityConstants;
  12. /**
  13. * An AccessControlContext is used to make system resource access decisions
  14. * based on the context it encapsulates.
  15. *
  16. * <p>More specifically, it encapsulates a context and
  17. * has a single method, <code>checkPermission</code>,
  18. * that is equivalent to the <code>checkPermission</code> method
  19. * in the AccessController class, with one difference: The AccessControlContext
  20. * <code>checkPermission</code> method makes access decisions based on the
  21. * context it encapsulates,
  22. * rather than that of the current execution thread.
  23. *
  24. * <p>Thus, the purpose of AccessControlContext is for those situations where
  25. * a security check that should be made within a given context
  26. * actually needs to be done from within a
  27. * <i>different</i> context (for example, from within a worker thread).
  28. *
  29. * <p> An AccessControlContext is created by calling the
  30. * <code>AccessController.getContext</code> method.
  31. * The <code>getContext</code> method takes a "snapshot"
  32. * of the current calling context, and places
  33. * it in an AccessControlContext object, which it returns. A sample call is
  34. * the following:
  35. *
  36. * <pre>
  37. *
  38. * AccessControlContext acc = AccessController.getContext()
  39. *
  40. * </pre>
  41. *
  42. * <p>
  43. * Code within a different context can subsequently call the
  44. * <code>checkPermission</code> method on the
  45. * previously-saved AccessControlContext object. A sample call is the
  46. * following:
  47. *
  48. * <pre>
  49. *
  50. * acc.checkPermission(permission)
  51. *
  52. * </pre>
  53. *
  54. * @see AccessController
  55. *
  56. * @author Roland Schemers
  57. */
  58. public final class AccessControlContext {
  59. private ProtectionDomain context[];
  60. private boolean isPrivileged;
  61. private AccessControlContext privilegedContext;
  62. private DomainCombiner combiner = null;
  63. private static boolean debugInit = false;
  64. private static Debug debug = null;
  65. static Debug getDebug()
  66. {
  67. if (debugInit)
  68. return debug;
  69. else {
  70. if (Policy.isSet()) {
  71. debug = Debug.getInstance("access");
  72. debugInit = true;
  73. }
  74. return debug;
  75. }
  76. }
  77. /**
  78. * Create an AccessControlContext with the given set of ProtectionDomains.
  79. * Context must not be null. Duplicate domains will be removed from the
  80. * context.
  81. *
  82. * @param context the ProtectionDomains associated with this context.
  83. * The non-duplicate domains are copied from the array. Subsequent
  84. * changes to the array will not affect this AccessControlContext.
  85. */
  86. public AccessControlContext(ProtectionDomain context[])
  87. {
  88. if (context.length == 0) {
  89. this.context = null;
  90. } else if (context.length == 1) {
  91. if (context[0] != null) {
  92. this.context = (ProtectionDomain[])context.clone();
  93. } else {
  94. this.context = null;
  95. }
  96. } else {
  97. List v = new ArrayList(context.length);
  98. for (int i =0; i< context.length; i++) {
  99. if ((context[i] != null) && (!v.contains(context[i])))
  100. v.add(context[i]);
  101. }
  102. this.context = new ProtectionDomain[v.size()];
  103. this.context = (ProtectionDomain[]) v.toArray(this.context);
  104. }
  105. }
  106. /**
  107. * Create a new <code>AccessControlContext</code> with the given
  108. * <code>AccessControlContext</code> and <code>DomainCombiner</code>.
  109. * This constructor associates the provided
  110. * <code>DomainCombiner</code> with the provided
  111. * <code>AccessControlContext</code>.
  112. *
  113. * <p>
  114. *
  115. * @param acc the <code>AccessControlContext</code> associated
  116. * with the provided <code>DomainCombiner</code>. <p>
  117. *
  118. * @param combiner the <code>DomainCombiner</code> to be associated
  119. * with the provided <code>AccessControlContext</code>.
  120. *
  121. * @exception NullPointerException if the provided
  122. * <code>context</code> is <code>null</code>. <p>
  123. *
  124. * @exception SecurityException if the caller does not have permission
  125. * to invoke this constructor.
  126. */
  127. public AccessControlContext(AccessControlContext acc,
  128. DomainCombiner combiner) {
  129. SecurityManager sm = System.getSecurityManager();
  130. if (sm != null) {
  131. sm.checkPermission(SecurityConstants.CREATE_ACC_PERMISSION);
  132. }
  133. this.context = acc.context;
  134. // we do not need to run the combine method on the
  135. // provided ACC. it was already "combined" when the
  136. // context was originally retrieved.
  137. //
  138. // at this point in time, we simply throw away the old
  139. // combiner and use the newly provided one.
  140. this.combiner = combiner;
  141. }
  142. /**
  143. * package private constructor for AccessController.getContext()
  144. */
  145. AccessControlContext(ProtectionDomain context[],
  146. boolean isPrivileged)
  147. {
  148. this.context = context;
  149. this.isPrivileged = isPrivileged;
  150. }
  151. /**
  152. * Returns true if this context is privileged.
  153. */
  154. boolean isPrivileged()
  155. {
  156. return isPrivileged;
  157. }
  158. /**
  159. * Get the <code>DomainCombiner</code> associated with this
  160. * <code>AccessControlContext</code>.
  161. *
  162. * <p>
  163. *
  164. * @return the <code>DomainCombiner</code> associated with this
  165. * <code>AccessControlContext</code>, or <code>null</code>
  166. * if there is none.
  167. *
  168. * @exception SecurityException if the caller does not have permission
  169. * to get the <code>DomainCombiner</code> associated with this
  170. * <code>AccessControlContext</code>.
  171. */
  172. public DomainCombiner getDomainCombiner() {
  173. SecurityManager sm = System.getSecurityManager();
  174. if (sm != null) {
  175. sm.checkPermission(SecurityConstants.GET_COMBINER_PERMISSION);
  176. }
  177. return combiner;
  178. }
  179. /**
  180. * Determines whether the access request indicated by the
  181. * specified permission should be allowed or denied, based on
  182. * the security policy currently in effect, and the context in
  183. * this object.
  184. * <p>
  185. * This method quietly returns if the access request
  186. * is permitted, or throws a suitable AccessControlException otherwise.
  187. *
  188. * @param perm the requested permission.
  189. *
  190. * @exception AccessControlException if the specified permission
  191. * is not permitted, based on the current security policy and the
  192. * context encapsulated by this object.
  193. * @exception NullPointerException if the permission to check for is null.
  194. */
  195. public void checkPermission(Permission perm)
  196. throws AccessControlException
  197. {
  198. if (perm == null) {
  199. throw new NullPointerException("permission can't be null");
  200. }
  201. if (getDebug() != null) {
  202. if (Debug.isOn("stack"))
  203. Thread.currentThread().dumpStack();
  204. if (Debug.isOn("domain")) {
  205. if (context == null) {
  206. debug.println("domain (context is null)");
  207. } else {
  208. for (int i=0; i< context.length; i++) {
  209. debug.println("domain "+i+" "+context[i]);
  210. }
  211. }
  212. }
  213. }
  214. /*
  215. * iterate through the ProtectionDomains in the context.
  216. * Stop at the first one that doesn't allow the
  217. * requested permission (throwing an exception).
  218. *
  219. */
  220. /* if ctxt is null, all we had on the stack were system domains,
  221. or the first domain was a Privileged system domain. This
  222. is to make the common case for system code very fast */
  223. if (context == null)
  224. return;
  225. for (int i=0; i< context.length; i++) {
  226. if (context[i] != null && !context[i].implies(perm)) {
  227. if (debug != null) {
  228. debug.println("access denied "+perm);
  229. if (Debug.isOn("failure")) {
  230. Thread.currentThread().dumpStack();
  231. final ProtectionDomain pd = context[i];
  232. final Debug db = debug;
  233. AccessController.doPrivileged (new PrivilegedAction() {
  234. public Object run() {
  235. db.println("domain that failed "+pd);
  236. return null;
  237. }
  238. });
  239. }
  240. }
  241. throw new AccessControlException("access denied "+perm, perm);
  242. }
  243. }
  244. // allow if all of them allowed access
  245. if (debug != null)
  246. debug.println("access allowed "+perm);
  247. return;
  248. }
  249. /**
  250. * Take the stack-based context (this) and combine it with the
  251. * privileged or inherited context, if need be.
  252. */
  253. AccessControlContext optimize() {
  254. // the assigned (privileged or inherited) context
  255. AccessControlContext acc;
  256. if (isPrivileged) {
  257. acc = privilegedContext;
  258. } else {
  259. acc = AccessController.getInheritedAccessControlContext();
  260. }
  261. // this.context could be null if only system code is on the stack;
  262. // in that case, ignore the stack context
  263. boolean skipStack = (context == null);
  264. // acc.context could be null if only system code was involved;
  265. // in that case, ignore the assigned context
  266. boolean skipAssigned = (acc == null || acc.context == null);
  267. // optimization: if neither have contexts; return acc if possible
  268. // rather than this, because acc might have a combiner
  269. if (skipAssigned && skipStack) {
  270. return (acc != null) ? acc : this;
  271. }
  272. if (acc != null && acc.combiner != null) {
  273. // let the assigned acc's combiner do its thing
  274. return goCombiner(context, acc);
  275. }
  276. // optimization: if there is no stack context; there is no reason
  277. // to compress the assigned context, it already is compressed
  278. if (skipStack) {
  279. return acc;
  280. }
  281. int slen = context.length;
  282. // optimization: if there is no assigned context and the stack length
  283. // is less then or equal to two; there is no reason to compress the
  284. // stack context, it already is
  285. if (skipAssigned && slen <= 2) {
  286. return this;
  287. }
  288. // optimization: if there is a single stack domain and that domain
  289. // is already in the assigned context; no need to combine
  290. if ((slen == 1) && (context[0] == acc.context[0])) {
  291. return acc;
  292. }
  293. int n = (skipAssigned) ? 0 : acc.context.length;
  294. // now we combine both of them, and create a new context
  295. ProtectionDomain pd[] = new ProtectionDomain[slen + n];
  296. // first copy in the assigned context domains, no need to compress
  297. if (!skipAssigned) {
  298. System.arraycopy(acc.context, 0, pd, 0, n);
  299. }
  300. // now add the stack context domains, discarding nulls and duplicates
  301. outer:
  302. for (int i = 0; i < context.length; i++) {
  303. ProtectionDomain sd = context[i];
  304. if (sd != null) {
  305. for (int j = 0; j < n; j++) {
  306. if (sd == pd[j]) {
  307. continue outer;
  308. }
  309. }
  310. pd[n++] = sd;
  311. }
  312. }
  313. // if length isn't equal, we need to shorten the array
  314. if (n != pd.length) {
  315. // optimization: if we didn't really combine anything
  316. if (!skipAssigned && n == acc.context.length) {
  317. return acc;
  318. } else if (skipAssigned && n == slen) {
  319. return this;
  320. }
  321. ProtectionDomain tmp[] = new ProtectionDomain[n];
  322. System.arraycopy(pd, 0, tmp, 0, n);
  323. pd = tmp;
  324. }
  325. // return new AccessControlContext(pd, false);
  326. // Reuse existing ACC
  327. this.context = pd;
  328. this.combiner = null;
  329. this.isPrivileged = false;
  330. return this;
  331. }
  332. private AccessControlContext goCombiner(ProtectionDomain[] current,
  333. AccessControlContext assigned) {
  334. // the assigned ACC's combiner is not null --
  335. // let the combiner do its thing
  336. // XXX we could add optimizations to 'current' here ...
  337. if (getDebug() != null) {
  338. debug.println("AccessControlContext invoking the Combiner");
  339. }
  340. // No need to clone current and assigned.context
  341. // combine() will not update them
  342. ProtectionDomain[] combinedPds = assigned.combiner.combine(
  343. current, assigned.context);
  344. // return new AccessControlContext(combinedPds, assigned.combiner);
  345. // Reuse existing ACC
  346. this.context = combinedPds;
  347. this.combiner = assigned.combiner;
  348. this.isPrivileged = false;
  349. return this;
  350. }
  351. /**
  352. * Checks two AccessControlContext objects for equality.
  353. * Checks that <i>obj</i> is
  354. * an AccessControlContext and has the same set of ProtectionDomains
  355. * as this context.
  356. * <P>
  357. * @param obj the object we are testing for equality with this object.
  358. * @return true if <i>obj</i> is an AccessControlContext, and has the
  359. * same set of ProtectionDomains as this context, false otherwise.
  360. */
  361. public boolean equals(Object obj) {
  362. if (obj == this)
  363. return true;
  364. if (! (obj instanceof AccessControlContext))
  365. return false;
  366. AccessControlContext that = (AccessControlContext) obj;
  367. if (context == null) {
  368. return (that.context == null);
  369. }
  370. if (that.context == null)
  371. return false;
  372. if (!(this.containsAllPDs(that) && that.containsAllPDs(this)))
  373. return false;
  374. if (this.combiner == null)
  375. return (that.combiner == null);
  376. if (that.combiner == null)
  377. return false;
  378. if (!this.combiner.equals(that.combiner))
  379. return false;
  380. return true;
  381. }
  382. private boolean containsAllPDs(AccessControlContext that) {
  383. boolean match = false;
  384. //
  385. // ProtectionDomains within an ACC currently cannot be null
  386. // and this is enforced by the contructor and the various
  387. // optimize methods. However, historically this logic made attempts
  388. // to support the notion of a null PD and therefore this logic continues
  389. // to support that notion.
  390. ProtectionDomain thisPd;
  391. for (int i = 0; i < context.length; i++) {
  392. match = false;
  393. if ((thisPd = context[i]) == null) {
  394. for (int j = 0; (j < that.context.length) && !match; j++) {
  395. match = (that.context[j] == null);
  396. }
  397. } else {
  398. Class thisPdClass = thisPd.getClass();
  399. ProtectionDomain thatPd;
  400. for (int j = 0; (j < that.context.length) && !match; j++) {
  401. thatPd = that.context[j];
  402. // Class check required to avoid PD exposure (4285406)
  403. match = (thatPd != null &&
  404. thisPdClass == thatPd.getClass() && thisPd.equals(thatPd));
  405. }
  406. }
  407. if (!match) return false;
  408. }
  409. return match;
  410. }
  411. /**
  412. * Returns the hash code value for this context. The hash code
  413. * is computed by exclusive or-ing the hash code of all the protection
  414. * domains in the context together.
  415. *
  416. * @return a hash code value for this context.
  417. */
  418. public int hashCode() {
  419. int hashCode = 0;
  420. if (context == null)
  421. return hashCode;
  422. for (int i =0; i < context.length; i++) {
  423. if (context[i] != null)
  424. hashCode ^= context[i].hashCode();
  425. }
  426. return hashCode;
  427. }
  428. }