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