KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > security > AccessControlContext


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  
8 package java.security;
9
10 import java.util.ArrayList JavaDoc;
11 import java.util.List JavaDoc;
12 import sun.security.util.Debug;
13 import sun.security.util.SecurityConstants;
14
15 /**
16  * An AccessControlContext is used to make system resource access decisions
17  * based on the context it encapsulates.
18  *
19  * <p>More specifically, it encapsulates a context and
20  * has a single method, <code>checkPermission</code>,
21  * that is equivalent to the <code>checkPermission</code> method
22  * in the AccessController class, with one difference: The AccessControlContext
23  * <code>checkPermission</code> method makes access decisions based on the
24  * context it encapsulates,
25  * rather than that of the current execution thread.
26  *
27  * <p>Thus, the purpose of AccessControlContext is for those situations where
28  * a security check that should be made within a given context
29  * actually needs to be done from within a
30  * <i>different</i> context (for example, from within a worker thread).
31  *
32  * <p> An AccessControlContext is created by calling the
33  * <code>AccessController.getContext</code> method.
34  * The <code>getContext</code> method takes a "snapshot"
35  * of the current calling context, and places
36  * it in an AccessControlContext object, which it returns. A sample call is
37  * the following:
38  *
39  * <pre>
40  *
41  * AccessControlContext acc = AccessController.getContext()
42  *
43  * </pre>
44  *
45  * <p>
46  * Code within a different context can subsequently call the
47  * <code>checkPermission</code> method on the
48  * previously-saved AccessControlContext object. A sample call is the
49  * following:
50  *
51  * <pre>
52  *
53  * acc.checkPermission(permission)
54  *
55  * </pre>
56  *
57  * @see AccessController
58  *
59  * @author Roland Schemers
60  */

61
62 public final class AccessControlContext {
63
64     private ProtectionDomain JavaDoc context[];
65     private boolean isPrivileged;
66     private AccessControlContext JavaDoc privilegedContext;
67     private DomainCombiner JavaDoc combiner = null;
68
69     private static boolean debugInit = false;
70     private static Debug debug = null;
71
72     static Debug getDebug()
73     {
74     if (debugInit)
75         return debug;
76     else {
77         if (Policy.isSet()) {
78         debug = Debug.getInstance("access");
79         debugInit = true;
80         }
81         return debug;
82     }
83     }
84
85     /**
86      * Create an AccessControlContext with the given set of ProtectionDomains.
87      * Context must not be null. Duplicate domains will be removed from the
88      * context.
89      *
90      * @param context the ProtectionDomains associated with this context.
91      * The non-duplicate domains are copied from the array. Subsequent
92      * changes to the array will not affect this AccessControlContext.
93      */

94     public AccessControlContext(ProtectionDomain JavaDoc context[])
95     {
96     if (context.length == 0) {
97         this.context = null;
98     } else if (context.length == 1) {
99         if (context[0] != null) {
100         this.context = (ProtectionDomain JavaDoc[])context.clone();
101         } else {
102         this.context = null;
103         }
104     } else {
105         List JavaDoc v = new ArrayList JavaDoc(context.length);
106         for (int i =0; i< context.length; i++) {
107         if ((context[i] != null) && (!v.contains(context[i])))
108             v.add(context[i]);
109         }
110         this.context = new ProtectionDomain JavaDoc[v.size()];
111         this.context = (ProtectionDomain JavaDoc[]) v.toArray(this.context);
112     }
113     }
114
115     /**
116      * Create a new <code>AccessControlContext</code> with the given
117      * <code>AccessControlContext</code> and <code>DomainCombiner</code>.
118      * This constructor associates the provided
119      * <code>DomainCombiner</code> with the provided
120      * <code>AccessControlContext</code>.
121      *
122      * <p>
123      *
124      * @param acc the <code>AccessControlContext</code> associated
125      * with the provided <code>DomainCombiner</code>. <p>
126      *
127      * @param combiner the <code>DomainCombiner</code> to be associated
128      * with the provided <code>AccessControlContext</code>.
129      *
130      * @exception NullPointerException if the provided
131      * <code>context</code> is <code>null</code>. <p>
132      *
133      * @exception SecurityException if the caller does not have permission
134      * to invoke this constructor.
135      */

136     public AccessControlContext(AccessControlContext JavaDoc acc,
137                 DomainCombiner JavaDoc combiner) {
138
139     SecurityManager JavaDoc sm = System.getSecurityManager();
140     if (sm != null) {
141         sm.checkPermission(SecurityConstants.CREATE_ACC_PERMISSION);
142     }
143
144     this.context = acc.context;
145
146     // we do not need to run the combine method on the
147
// provided ACC. it was already "combined" when the
148
// context was originally retrieved.
149
//
150
// at this point in time, we simply throw away the old
151
// combiner and use the newly provided one.
152
this.combiner = combiner;
153     }
154
155     /**
156      * package private constructor for AccessController.getContext()
157      */

158
159     AccessControlContext(ProtectionDomain JavaDoc context[],
160                  boolean isPrivileged)
161     {
162     this.context = context;
163     this.isPrivileged = isPrivileged;
164     }
165
166     /**
167      * Returns true if this context is privileged.
168      */

169     boolean isPrivileged()
170     {
171     return isPrivileged;
172
173     }
174
175     /**
176      * Get the <code>DomainCombiner</code> associated with this
177      * <code>AccessControlContext</code>.
178      *
179      * <p>
180      *
181      * @return the <code>DomainCombiner</code> associated with this
182      * <code>AccessControlContext</code>, or <code>null</code>
183      * if there is none.
184      *
185      * @exception SecurityException if the caller does not have permission
186      * to get the <code>DomainCombiner</code> associated with this
187      * <code>AccessControlContext</code>.
188      */

189     public DomainCombiner JavaDoc getDomainCombiner() {
190
191     SecurityManager JavaDoc sm = System.getSecurityManager();
192     if (sm != null) {
193         sm.checkPermission(SecurityConstants.GET_COMBINER_PERMISSION);
194     }
195     return combiner;
196     }
197
198     /**
199      * Determines whether the access request indicated by the
200      * specified permission should be allowed or denied, based on
201      * the security policy currently in effect, and the context in
202      * this object.
203      * <p>
204      * This method quietly returns if the access request
205      * is permitted, or throws a suitable AccessControlException otherwise.
206      *
207      * @param perm the requested permission.
208      *
209      * @exception AccessControlException if the specified permission
210      * is not permitted, based on the current security policy and the
211      * context encapsulated by this object.
212      * @exception NullPointerException if the permission to check for is null.
213      */

214     public void checkPermission(Permission JavaDoc perm)
215     throws AccessControlException JavaDoc
216     {
217     if (perm == null) {
218         throw new NullPointerException JavaDoc("permission can't be null");
219     }
220     if (getDebug() != null) {
221         if (Debug.isOn("stack"))
222             Thread.currentThread().dumpStack();
223         if (Debug.isOn("domain")) {
224         if (context == null) {
225             debug.println("domain (context is null)");
226         } else {
227             for (int i=0; i< context.length; i++) {
228             debug.println("domain "+i+" "+context[i]);
229             }
230         }
231         }
232     }
233
234     /*
235      * iterate through the ProtectionDomains in the context.
236      * Stop at the first one that doesn't allow the
237      * requested permission (throwing an exception).
238      *
239      */

240
241     /* if ctxt is null, all we had on the stack were system domains,
242        or the first domain was a Privileged system domain. This
243        is to make the common case for system code very fast */

244
245     if (context == null)
246         return;
247
248     for (int i=0; i< context.length; i++) {
249         if (context[i] != null && !context[i].implies(perm)) {
250         if (debug != null) {
251             debug.println("access denied "+perm);
252             if (Debug.isOn("failure")) {
253             Thread.currentThread().dumpStack();
254             final ProtectionDomain JavaDoc pd = context[i];
255             final Debug db = debug;
256             AccessController.doPrivileged (new PrivilegedAction JavaDoc() {
257                 public Object JavaDoc run() {
258                 db.println("domain that failed "+pd);
259                 return null;
260                 }
261             });
262             }
263         }
264         throw new AccessControlException JavaDoc("access denied "+perm, perm);
265         }
266     }
267
268     // allow if all of them allowed access
269
if (debug != null)
270         debug.println("access allowed "+perm);
271
272     return;
273     }
274
275     /**
276      * Take the stack-based context (this) and combine it with the
277      * privileged or inherited context, if need be.
278      */

279     AccessControlContext JavaDoc optimize() {
280     // the assigned (privileged or inherited) context
281
AccessControlContext JavaDoc acc;
282     if (isPrivileged) {
283         acc = privilegedContext;
284     } else {
285         acc = AccessController.getInheritedAccessControlContext();
286     }
287
288     // this.context could be null if only system code is on the stack;
289
// in that case, ignore the stack context
290
boolean skipStack = (context == null);
291
292     // acc.context could be null if only system code was involved;
293
// in that case, ignore the assigned context
294
boolean skipAssigned = (acc == null || acc.context == null);
295
296     // optimization: if neither have contexts; return acc if possible
297
// rather than this, because acc might have a combiner
298
if (skipAssigned && skipStack) {
299         return (acc != null) ? acc : this;
300     }
301
302     if (acc != null && acc.combiner != null) {
303         // let the assigned acc's combiner do its thing
304
return goCombiner(context, acc);
305     }
306
307     // optimization: if there is no stack context; there is no reason
308
// to compress the assigned context, it already is compressed
309
if (skipStack) {
310         return acc;
311     }
312
313     int slen = context.length;
314
315     // optimization: if there is no assigned context and the stack length
316
// is less then or equal to two; there is no reason to compress the
317
// stack context, it already is
318
if (skipAssigned && slen <= 2) {
319         return this;
320     }
321
322     // optimization: if there is a single stack domain and that domain
323
// is already in the assigned context; no need to combine
324
if ((slen == 1) && (context[0] == acc.context[0])) {
325         return acc;
326     }
327
328     int n = (skipAssigned) ? 0 : acc.context.length;
329
330     // now we combine both of them, and create a new context
331
ProtectionDomain JavaDoc pd[] = new ProtectionDomain JavaDoc[slen + n];
332
333     // first copy in the assigned context domains, no need to compress
334
if (!skipAssigned) {
335         System.arraycopy(acc.context, 0, pd, 0, n);
336     }
337
338     // now add the stack context domains, discarding nulls and duplicates
339
outer:
340     for (int i = 0; i < context.length; i++) {
341         ProtectionDomain JavaDoc sd = context[i];
342         if (sd != null) {
343         for (int j = 0; j < n; j++) {
344             if (sd == pd[j]) {
345             continue outer;
346             }
347         }
348         pd[n++] = sd;
349         }
350     }
351
352     // if length isn't equal, we need to shorten the array
353
if (n != pd.length) {
354         // optimization: if we didn't really combine anything
355
if (!skipAssigned && n == acc.context.length) {
356         return acc;
357         } else if (skipAssigned && n == slen) {
358         return this;
359         }
360         ProtectionDomain JavaDoc tmp[] = new ProtectionDomain JavaDoc[n];
361         System.arraycopy(pd, 0, tmp, 0, n);
362         pd = tmp;
363     }
364
365     // return new AccessControlContext(pd, false);
366

367     // Reuse existing ACC
368

369     this.context = pd;
370     this.combiner = null;
371     this.isPrivileged = false;
372
373     return this;
374     }
375
376     private AccessControlContext JavaDoc goCombiner(ProtectionDomain JavaDoc[] current,
377                     AccessControlContext JavaDoc assigned) {
378
379     // the assigned ACC's combiner is not null --
380
// let the combiner do its thing
381

382     // XXX we could add optimizations to 'current' here ...
383

384     if (getDebug() != null) {
385         debug.println("AccessControlContext invoking the Combiner");
386     }
387
388     // No need to clone current and assigned.context
389
// combine() will not update them
390
ProtectionDomain JavaDoc[] combinedPds = assigned.combiner.combine(
391         current, assigned.context);
392
393     // return new AccessControlContext(combinedPds, assigned.combiner);
394

395     // Reuse existing ACC
396
this.context = combinedPds;
397     this.combiner = assigned.combiner;
398     this.isPrivileged = false;
399
400     return this;
401     }
402
403     /**
404      * Checks two AccessControlContext objects for equality.
405      * Checks that <i>obj</i> is
406      * an AccessControlContext and has the same set of ProtectionDomains
407      * as this context.
408      * <P>
409      * @param obj the object we are testing for equality with this object.
410      * @return true if <i>obj</i> is an AccessControlContext, and has the
411      * same set of ProtectionDomains as this context, false otherwise.
412      */

413     public boolean equals(Object JavaDoc obj) {
414     if (obj == this)
415         return true;
416
417     if (! (obj instanceof AccessControlContext JavaDoc))
418         return false;
419
420     AccessControlContext JavaDoc that = (AccessControlContext JavaDoc) obj;
421
422
423     if (context == null) {
424         return (that.context == null);
425     }
426
427     if (that.context == null)
428         return false;
429
430     if (!(this.containsAllPDs(that) && that.containsAllPDs(this)))
431         return false;
432
433     if (this.combiner == null)
434         return (that.combiner == null);
435
436     if (that.combiner == null)
437         return false;
438
439     if (!this.combiner.equals(that.combiner))
440         return false;
441
442     return true;
443     }
444
445     private boolean containsAllPDs(AccessControlContext JavaDoc that) {
446     boolean match = false;
447     //
448
// ProtectionDomains within an ACC currently cannot be null
449
// and this is enforced by the contructor and the various
450
// optimize methods. However, historically this logic made attempts
451
// to support the notion of a null PD and therefore this logic continues
452
// to support that notion.
453
ProtectionDomain JavaDoc thisPd;
454     for (int i = 0; i < context.length; i++) {
455         match = false;
456         if ((thisPd = context[i]) == null) {
457         for (int j = 0; (j < that.context.length) && !match; j++) {
458             match = (that.context[j] == null);
459         }
460         } else {
461         Class JavaDoc thisPdClass = thisPd.getClass();
462         ProtectionDomain JavaDoc thatPd;
463         for (int j = 0; (j < that.context.length) && !match; j++) {
464             thatPd = that.context[j];
465
466             // Class check required to avoid PD exposure (4285406)
467
match = (thatPd != null &&
468             thisPdClass == thatPd.getClass() && thisPd.equals(thatPd));
469         }
470         }
471         if (!match) return false;
472     }
473     return match;
474     }
475     /**
476      * Returns the hash code value for this context. The hash code
477      * is computed by exclusive or-ing the hash code of all the protection
478      * domains in the context together.
479      *
480      * @return a hash code value for this context.
481      */

482
483     public int hashCode() {
484     int hashCode = 0;
485
486     if (context == null)
487         return hashCode;
488
489     for (int i =0; i < context.length; i++) {
490         if (context[i] != null)
491         hashCode ^= context[i].hashCode();
492     }
493     return hashCode;
494     }
495 }
496
Popular Tags