1. /*
  2. * @(#)AccessControlContext.java 1.30 00/02/02
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.security;
  11. import java.util.Vector;
  12. import sun.security.util.Debug;
  13. /**
  14. * An AccessControlContext is used to make system resource access decisions
  15. * based on the context it encapsulates.
  16. *
  17. * <p>More specifically, it encapsulates a context and
  18. * has a single method, <code>checkPermission</code>,
  19. * that is equivalent to the <code>checkPermission</code> method
  20. * in the AccessController class, with one difference: The AccessControlContext
  21. * <code>checkPermission</code> method makes access decisions based on the
  22. * context it encapsulates,
  23. * rather than that of the current execution thread.
  24. *
  25. * <p>Thus, the purpose of AccessControlContext is for those situations where
  26. * a security check that should be made within a given context
  27. * actually needs to be done from within a
  28. * <i>different</i> context (for example, from within a worker thread).
  29. *
  30. * <p> An AccessControlContext is created by calling the
  31. * <code>AccessController.getContext</code> method.
  32. * The <code>getContext</code> method takes a "snapshot"
  33. * of the current calling context, and places
  34. * it in an AccessControlContext object, which it returns. A sample call is
  35. * the following:
  36. *
  37. * <pre>
  38. *
  39. * AccessControlContext acc = AccessController.getContext()
  40. *
  41. * </pre>
  42. *
  43. * <p>
  44. * Code within a different context can subsequently call the
  45. * <code>checkPermission</code> method on the
  46. * previously-saved AccessControlContext object. A sample call is the
  47. * following:
  48. *
  49. * <pre>
  50. *
  51. * acc.checkPermission(permission)
  52. *
  53. * </pre>
  54. *
  55. * @see AccessController
  56. *
  57. * @author Roland Schemers
  58. */
  59. public final class AccessControlContext {
  60. private ProtectionDomain context[];
  61. private boolean isPrivileged;
  62. private AccessControlContext privilegedContext;
  63. private DomainCombiner combiner;
  64. private static boolean debugInit = false;
  65. private static Debug debug = null;
  66. static Debug getDebug()
  67. {
  68. if (debugInit)
  69. return debug;
  70. else {
  71. if (Policy.isSet()) {
  72. debug = Debug.getInstance("access");
  73. debugInit = true;
  74. }
  75. return debug;
  76. }
  77. }
  78. /**
  79. * Create an AccessControlContext with the given set of ProtectionDomains.
  80. * Context must not be null. Duplicate domains will be removed from the
  81. * context.
  82. *
  83. * @param context the ProtectionDomains associated with this context.
  84. */
  85. public AccessControlContext(ProtectionDomain context[])
  86. {
  87. 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 either the provided
  115. * <code>context</code> or the provided
  116. * <code>combiner</code> are <code>null</code>. <p>
  117. *
  118. * @exception SecurityException if the caller does not have permission
  119. * to invoke this constructor.
  120. */
  121. public AccessControlContext(AccessControlContext acc,
  122. DomainCombiner combiner) {
  123. SecurityManager sm = System.getSecurityManager();
  124. if (sm != null) {
  125. sm.checkPermission(new SecurityPermission
  126. ("createAccessControlContext"));
  127. }
  128. if (acc == null || combiner == null) {
  129. throw new NullPointerException
  130. ("null AccessControlContext or DomainCombiner was provided");
  131. }
  132. this.context = acc.context;
  133. // we do not need to run the combine method on the
  134. // provided ACC. it was already "combined" when the
  135. // context was originally retrieved.
  136. //
  137. // at this point in time, we simply throw away the old
  138. // combiner and use the newly provided one.
  139. this.combiner = combiner;
  140. }
  141. private AccessControlContext(ProtectionDomain context[],
  142. DomainCombiner combiner) {
  143. this.context = (ProtectionDomain[])context.clone();
  144. this.combiner = combiner;
  145. }
  146. /**
  147. * package private constructor for AccessController.getContext()
  148. */
  149. AccessControlContext(ProtectionDomain context[],
  150. boolean isPrivileged)
  151. {
  152. this.context = context;
  153. this.isPrivileged = isPrivileged;
  154. }
  155. /**
  156. * Returns true if this context is privileged.
  157. */
  158. boolean isPrivileged()
  159. {
  160. return isPrivileged;
  161. }
  162. /**
  163. * Get the <code>DomainCombiner</code> associated with this
  164. * <code>AccessControlContext</code>.
  165. *
  166. * <p>
  167. *
  168. * @return the <code>DomainCombiner</code> associated with this
  169. * <code>AccessControlContext</code>, or <code>null</code>
  170. * if there is none.
  171. *
  172. * @exception SecurityException if the caller does not have permission
  173. * to get the <code>DomainCombiner</code> associated with this
  174. * <code>AccessControlContext</code>.
  175. */
  176. public DomainCombiner getDomainCombiner() {
  177. SecurityManager sm = System.getSecurityManager();
  178. if (sm != null) {
  179. sm.checkPermission(new SecurityPermission
  180. ("getDomainCombiner"));
  181. }
  182. return combiner;
  183. }
  184. /**
  185. * Determines whether the access request indicated by the
  186. * specified permission should be allowed or denied, based on
  187. * the security policy currently in effect, and the context in
  188. * this object.
  189. * <p>
  190. * This method quietly returns if the access request
  191. * is permitted, or throws a suitable AccessControlException otherwise.
  192. *
  193. * @param perm the requested permission.
  194. *
  195. * @exception AccessControlException if the specified permission
  196. * is not permitted, based on the current security policy and the
  197. * context encapsulated by this object.
  198. * @exception NullPointerException if the permission to check for is null.
  199. */
  200. public void checkPermission(Permission perm)
  201. throws AccessControlException
  202. {
  203. if (perm == null) {
  204. throw new NullPointerException("permission can't be null");
  205. }
  206. if (getDebug() != null) {
  207. if (Debug.isOn("stack"))
  208. Thread.currentThread().dumpStack();
  209. if (Debug.isOn("domain")) {
  210. if (context == null) {
  211. debug.println("domain (context is null)");
  212. } else {
  213. for (int i=0; i< context.length; i++) {
  214. debug.println("domain "+i+" "+context[i]);
  215. }
  216. }
  217. }
  218. }
  219. /*
  220. * iterate through the ProtectionDomains in the context.
  221. * Stop at the first one that doesn't allow the
  222. * requested permission (throwing an exception).
  223. *
  224. */
  225. /* if ctxt is null, all we had on the stack were system domains,
  226. or the first domain was a Privileged system domain. This
  227. is to make the common case for system code very fast */
  228. if (context == null)
  229. return;
  230. for (int i=0; i< context.length; i++) {
  231. if (context[i] != null && !context[i].implies(perm)) {
  232. if (debug != null) {
  233. debug.println("access denied "+perm);
  234. if (Debug.isOn("failure")) {
  235. Thread.currentThread().dumpStack();
  236. final ProtectionDomain pd = context[i];
  237. final Debug db = debug;
  238. AccessController.doPrivileged (new PrivilegedAction() {
  239. public Object run() {
  240. db.println("domain that failed "+pd);
  241. return null;
  242. }
  243. });
  244. }
  245. }
  246. throw new AccessControlException("access denied "+perm, perm);
  247. }
  248. }
  249. // allow if all of them allowed access
  250. if (debug != null)
  251. debug.println("access allowed "+perm);
  252. return;
  253. }
  254. /**
  255. * Take the stack-based context (this) and combine it with
  256. * the privileged context. this method will only be called
  257. * if privilegedContext is non-null.
  258. */
  259. AccessControlContext combineWithPrivilegedContext()
  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. AccessControlContext pacc = privilegedContext;
  265. boolean skipPrivileged = (pacc.context == null);
  266. if (skipPrivileged && skipStack && pacc.combiner == null) {
  267. return this;
  268. }
  269. if (pacc.combiner != null) {
  270. // the Privileged AccessControlContext's combiner is not null --
  271. // let the combiner do its thing
  272. return goCombiner(context, pacc, true);
  273. } else {
  274. int slen = (skipStack) ? 0 : context.length;
  275. // optimization: if the length is less then or equal to two,
  276. // there is no reason to compress the stack context, it already is
  277. if (skipPrivileged && slen <= 2)
  278. return this;
  279. int plen = (skipPrivileged) ? 0 : pacc.context.length;
  280. // optimization: if the length is less then or equal to two,
  281. // there is no reason to compress the priv context, it already is
  282. if (skipStack && plen <= 2)
  283. return pacc;
  284. // optimization: case where we have a length of 1 and
  285. // protection domains for priv context and stack are equal
  286. if ((slen == 1) && (plen == 1) && (context[0] == pacc.context[0]))
  287. return this;
  288. // now we combine both of them, and create a new context.
  289. ProtectionDomain pd[] = new ProtectionDomain[slen + plen];
  290. int i, j, n;
  291. n = 0;
  292. // first add all the protection domains from the stack context,
  293. // throwing out nulls and duplicates
  294. if (!skipStack) {
  295. for (i = 0; i < context.length; i++) {
  296. boolean add = true;
  297. for (j= 0; (j < n) && add; j++) {
  298. add = (context[i] != null) && (context[i] != pd[j]);
  299. }
  300. if (add) {
  301. pd[n++] = context[i];
  302. }
  303. }
  304. }
  305. // now add all the protection domains from the priv context,
  306. // throwing out nulls and duplicates
  307. if (!skipPrivileged) {
  308. for (i = 0; i < pacc.context.length; i++) {
  309. boolean add = true;
  310. for (j= 0; (j < n) && add; j++) {
  311. add = (pacc.context[i] != null) &&
  312. (pacc.context[i] != pd[j]);
  313. }
  314. if (add) {
  315. pd[n++] = pacc.context[i];
  316. }
  317. }
  318. }
  319. // if length isn't equal, we need to shorten the array
  320. if (n != pd.length) {
  321. // if all we had were system domains, context is null
  322. if (n == 0) {
  323. pd = null;
  324. } else {
  325. ProtectionDomain tmp[] = new ProtectionDomain[n];
  326. System.arraycopy(pd, 0, tmp, 0, n);
  327. pd = tmp;
  328. }
  329. }
  330. return new AccessControlContext(pd, true);
  331. }
  332. }
  333. /**
  334. * Take the stack-based context (this) and combine it with
  335. * the inherited context, if need be.
  336. */
  337. AccessControlContext optimize()
  338. {
  339. // this.context could be null if only system code is on the stack
  340. // in that case, ignore the stack context
  341. boolean skipStack = (context == null);
  342. // if this context is privileged,
  343. // or if tacc is null, or if tacc.context is null,
  344. // don't do the thread context
  345. boolean skipThread;
  346. AccessControlContext tacc;
  347. if (isPrivileged) {
  348. if (privilegedContext != null)
  349. return combineWithPrivilegedContext();
  350. else {
  351. skipThread = true;
  352. tacc = null;
  353. }
  354. } else {
  355. tacc = AccessController.getInheritedAccessControlContext();
  356. skipThread = (tacc == null) ||
  357. (tacc.context == null && tacc.combiner == null);
  358. }
  359. if (skipThread && skipStack) {
  360. return this;
  361. }
  362. if (tacc != null && tacc.combiner != null) {
  363. // the inherited Thread AccessControlContext's combiner
  364. // is not null -- let the combiner do its thing
  365. return goCombiner(context, tacc, false);
  366. } else {
  367. int slen = (skipStack) ? 0 : context.length;
  368. // optimization: if the length is less then or equal to two,
  369. // there is no reason to compress the stack context, it already is
  370. if (skipThread && slen <= 2)
  371. return this;
  372. int tlen = (skipThread) ? 0 : tacc.context.length;
  373. // optimization: if the length is less then or equal to two,
  374. // there is no reason to compress the thread context, it already is
  375. if (skipStack && tlen <= 2)
  376. return tacc;
  377. // optimization: case where we have a length of 1 and
  378. // protection domains for thread and stack are equal
  379. if ((slen == 1) && (tlen == 1) && (context[0] == tacc.context[0]))
  380. return this;
  381. // now we combine both of them, and create a new context.
  382. ProtectionDomain pd[] = new ProtectionDomain[slen + tlen];
  383. int i, j, n;
  384. n = 0;
  385. // first add all the protection domains from the stack context,
  386. // throwing out nulls and duplicates
  387. if (!skipStack) {
  388. for (i = 0; i < context.length; i++) {
  389. boolean add = true;
  390. for (j= 0; (j < n) && add; j++) {
  391. add = (context[i] != null) && (context[i] != pd[j]);
  392. }
  393. if (add) {
  394. pd[n++] = context[i];
  395. }
  396. }
  397. }
  398. // now add all the protection domains from the inherited context,
  399. // throwing out nulls and duplicates
  400. // only do if stack context is not privileged, and the thread
  401. // context is not null.
  402. if (!skipThread) {
  403. for (i = 0; i < tacc.context.length; i++) {
  404. boolean add = true;
  405. for (j= 0; (j < n) && add; j++) {
  406. add = (tacc.context[i] != null) &&
  407. (tacc.context[i] != pd[j]);
  408. }
  409. if (add) {
  410. pd[n++] = tacc.context[i];
  411. }
  412. }
  413. }
  414. // if length isn't equal, we need to shorten the array
  415. if (n != pd.length) {
  416. // if all we had were system domains, context is null
  417. if (n == 0) {
  418. pd = null;
  419. } else {
  420. ProtectionDomain tmp[] = new ProtectionDomain[n];
  421. System.arraycopy(pd, 0, tmp, 0, n);
  422. pd = tmp;
  423. }
  424. }
  425. return new AccessControlContext(pd, isPrivileged);
  426. }
  427. }
  428. private AccessControlContext goCombiner(ProtectionDomain[] current,
  429. AccessControlContext assigned,
  430. boolean doPriv) {
  431. // the assigned ACC's combiner is not null --
  432. // let the combiner do its thing
  433. // XXX we could add optimizations to 'current' here ...
  434. if (getDebug() != null) {
  435. debug.println("AccessControlContext invoking the Combiner");
  436. }
  437. ProtectionDomain[] combinedPds = assigned.combiner.combine
  438. (current == null ?
  439. null :
  440. (ProtectionDomain[])current.clone(),
  441. assigned.context == null ?
  442. null :
  443. (ProtectionDomain[])assigned.context.clone());
  444. // return the new ACC
  445. return new AccessControlContext(combinedPds, assigned.combiner);
  446. }
  447. /**
  448. * Checks two AccessControlContext objects for equality.
  449. * Checks that <i>obj</i> is
  450. * an AccessControlContext and has the same set of ProtectionDomains
  451. * as this context.
  452. * <P>
  453. * @param obj the object we are testing for equality with this object.
  454. * @return true if <i>obj</i> is an AccessControlContext, and has the
  455. * same set of ProtectionDomains as this context, false otherwise.
  456. */
  457. public boolean equals(Object obj) {
  458. if (obj == this)
  459. return true;
  460. if (! (obj instanceof AccessControlContext))
  461. return false;
  462. AccessControlContext that = (AccessControlContext) obj;
  463. if (context == null) {
  464. return (that.context == null);
  465. }
  466. if (that.context == null)
  467. return false;
  468. if (!(this.containsAllPDs(that) && that.containsAllPDs(this)))
  469. return false;
  470. if (this.combiner == null)
  471. return (that.combiner == null);
  472. if (that.combiner == null)
  473. return false;
  474. if (!this.combiner.equals(that.combiner))
  475. return false;
  476. return true;
  477. }
  478. private boolean containsAllPDs(AccessControlContext that) {
  479. boolean match = false;
  480. //
  481. // ProtectionDomains within an ACC currently cannot be null
  482. // and this is enforced by the contructor and the various
  483. // optimize methods. However, historically this logic made attempts
  484. // to support the notion of a null PD and therefore this logic continues
  485. // to support that notion.
  486. for (int i = 0; i < context.length; i++) {
  487. match = false;
  488. if (context[i] == null) {
  489. for (int j = 0; (j < that.context.length) && !match; j++) {
  490. match = (that.context[j] == null);
  491. }
  492. } else {
  493. for (int j = 0; (j < that.context.length) && !match; j++) {
  494. if (that.context[j] != null) {
  495. match =
  496. ((context[i].getClass()==that.context[j].getClass()) &&
  497. (context[i].equals(that.context[j])));
  498. }
  499. }
  500. }
  501. if (!match) return false;
  502. }
  503. return match;
  504. }
  505. /**
  506. * Returns the hash code value for this context. The hash code
  507. * is computed by exclusive or-ing the hash code of all the protection
  508. * domains in the context together.
  509. *
  510. * @return a hash code value for this context.
  511. */
  512. public int hashCode() {
  513. int hashCode = 0;
  514. if (context == null)
  515. return hashCode;
  516. for (int i =0; i < context.length; i++) {
  517. if (context[i] != null)
  518. hashCode ^= context[i].hashCode();
  519. }
  520. return hashCode;
  521. }
  522. }