View Javadoc
1   /*
2    * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package java.security;
27  
28  import java.util.ArrayList;
29  import java.util.List;
30  import sun.security.util.Debug;
31  import sun.security.util.SecurityConstants;
32  
33  
34  /**
35   * An AccessControlContext is used to make system resource access decisions
36   * based on the context it encapsulates.
37   *
38   * <p>More specifically, it encapsulates a context and
39   * has a single method, {@code checkPermission},
40   * that is equivalent to the {@code checkPermission} method
41   * in the AccessController class, with one difference: The AccessControlContext
42   * {@code checkPermission} method makes access decisions based on the
43   * context it encapsulates,
44   * rather than that of the current execution thread.
45   *
46   * <p>Thus, the purpose of AccessControlContext is for those situations where
47   * a security check that should be made within a given context
48   * actually needs to be done from within a
49   * <i>different</i> context (for example, from within a worker thread).
50   *
51   * <p> An AccessControlContext is created by calling the
52   * {@code AccessController.getContext} method.
53   * The {@code getContext} method takes a "snapshot"
54   * of the current calling context, and places
55   * it in an AccessControlContext object, which it returns. A sample call is
56   * the following:
57   *
58   * <pre>
59   *   AccessControlContext acc = AccessController.getContext()
60   * </pre>
61   *
62   * <p>
63   * Code within a different context can subsequently call the
64   * {@code checkPermission} method on the
65   * previously-saved AccessControlContext object. A sample call is the
66   * following:
67   *
68   * <pre>
69   *   acc.checkPermission(permission)
70   * </pre>
71   *
72   * @see AccessController
73   *
74   * @author Roland Schemers
75   */
76  
77  public final class AccessControlContext {
78  
79      private ProtectionDomain context[];
80      // isPrivileged and isAuthorized are referenced by the VM - do not remove
81      // or change their names
82      private boolean isPrivileged;
83      private boolean isAuthorized = false;
84  
85      // Note: This field is directly used by the virtual machine
86      // native codes. Don't touch it.
87      private AccessControlContext privilegedContext;
88  
89      private DomainCombiner combiner = null;
90  
91      // limited privilege scope
92      private Permission permissions[];
93      private AccessControlContext parent;
94      private boolean isWrapped;
95  
96      // is constrained by limited privilege scope?
97      private boolean isLimited;
98      private ProtectionDomain limitedContext[];
99  
100     private static boolean debugInit = false;
101     private static Debug debug = null;
102 
103     static Debug getDebug()
104     {
105         if (debugInit)
106             return debug;
107         else {
108             if (Policy.isSet()) {
109                 debug = Debug.getInstance("access");
110                 debugInit = true;
111             }
112             return debug;
113         }
114     }
115 
116     /**
117      * Create an AccessControlContext with the given array of ProtectionDomains.
118      * Context must not be null. Duplicate domains will be removed from the
119      * context.
120      *
121      * @param context the ProtectionDomains associated with this context.
122      * The non-duplicate domains are copied from the array. Subsequent
123      * changes to the array will not affect this AccessControlContext.
124      * @throws NullPointerException if {@code context} is {@code null}
125      */
126     public AccessControlContext(ProtectionDomain context[])
127     {
128         if (context.length == 0) {
129             this.context = null;
130         } else if (context.length == 1) {
131             if (context[0] != null) {
132                 this.context = context.clone();
133             } else {
134                 this.context = null;
135             }
136         } else {
137             List<ProtectionDomain> v = new ArrayList<>(context.length);
138             for (int i =0; i< context.length; i++) {
139                 if ((context[i] != null) &&  (!v.contains(context[i])))
140                     v.add(context[i]);
141             }
142             if (!v.isEmpty()) {
143                 this.context = new ProtectionDomain[v.size()];
144                 this.context = v.toArray(this.context);
145             }
146         }
147     }
148 
149     /**
150      * Create a new {@code AccessControlContext} with the given
151      * {@code AccessControlContext} and {@code DomainCombiner}.
152      * This constructor associates the provided
153      * {@code DomainCombiner} with the provided
154      * {@code AccessControlContext}.
155      *
156      * <p>
157      *
158      * @param acc the {@code AccessControlContext} associated
159      *          with the provided {@code DomainCombiner}.
160      *
161      * @param combiner the {@code DomainCombiner} to be associated
162      *          with the provided {@code AccessControlContext}.
163      *
164      * @exception NullPointerException if the provided
165      *          {@code context} is {@code null}.
166      *
167      * @exception SecurityException if a security manager is installed and the
168      *          caller does not have the "createAccessControlContext"
169      *          {@link SecurityPermission}
170      * @since 1.3
171      */
172     public AccessControlContext(AccessControlContext acc,
173                                 DomainCombiner combiner) {
174 
175         SecurityManager sm = System.getSecurityManager();
176         if (sm != null) {
177             sm.checkPermission(SecurityConstants.CREATE_ACC_PERMISSION);
178             this.isAuthorized = true;
179         }
180 
181         this.context = acc.context;
182 
183         // we do not need to run the combine method on the
184         // provided ACC.  it was already "combined" when the
185         // context was originally retrieved.
186         //
187         // at this point in time, we simply throw away the old
188         // combiner and use the newly provided one.
189         this.combiner = combiner;
190     }
191 
192     /**
193      * package private for AccessController
194      *
195      * This "argument wrapper" context will be passed as the actual context
196      * parameter on an internal doPrivileged() call used in the implementation.
197      */
198     AccessControlContext(ProtectionDomain caller, DomainCombiner combiner,
199         AccessControlContext parent, AccessControlContext context,
200         Permission[] perms)
201     {
202         /*
203          * Combine the domains from the doPrivileged() context into our
204          * wrapper context, if necessary.
205          */
206         ProtectionDomain[] callerPDs = null;
207         if (caller != null) {
208              callerPDs = new ProtectionDomain[] { caller };
209         }
210         if (context != null) {
211             if (combiner != null) {
212                 this.context = combiner.combine(callerPDs, context.context);
213             } else {
214                 this.context = combine(callerPDs, context.context);
215             }
216         } else {
217             /*
218              * Call combiner even if there is seemingly nothing to combine.
219              */
220             if (combiner != null) {
221                 this.context = combiner.combine(callerPDs, null);
222             } else {
223                 this.context = combine(callerPDs, null);
224             }
225         }
226         this.combiner = combiner;
227 
228         Permission[] tmp = null;
229         if (perms != null) {
230             tmp = new Permission[perms.length];
231             for (int i=0; i < perms.length; i++) {
232                 if (perms[i] == null) {
233                     throw new NullPointerException("permission can't be null");
234                 }
235 
236                 /*
237                  * An AllPermission argument is equivalent to calling
238                  * doPrivileged() without any limit permissions.
239                  */
240                 if (perms[i].getClass() == AllPermission.class) {
241                     parent = null;
242                 }
243                 tmp[i] = perms[i];
244             }
245         }
246 
247         /*
248          * For a doPrivileged() with limited privilege scope, initialize
249          * the relevant fields.
250          *
251          * The limitedContext field contains the union of all domains which
252          * are enclosed by this limited privilege scope. In other words,
253          * it contains all of the domains which could potentially be checked
254          * if none of the limiting permissions implied a requested permission.
255          */
256         if (parent != null) {
257             this.limitedContext = combine(parent.context, parent.limitedContext);
258             this.isLimited = true;
259             this.isWrapped = true;
260             this.permissions = tmp;
261             this.parent = parent;
262             this.privilegedContext = context; // used in checkPermission2()
263         }
264         this.isAuthorized = true;
265     }
266 
267 
268     /**
269      * package private constructor for AccessController.getContext()
270      */
271 
272     AccessControlContext(ProtectionDomain context[],
273                          boolean isPrivileged)
274     {
275         this.context = context;
276         this.isPrivileged = isPrivileged;
277         this.isAuthorized = true;
278     }
279 
280     /**
281      * Constructor for JavaSecurityAccess.doIntersectionPrivilege()
282      */
283     AccessControlContext(ProtectionDomain[] context,
284                          AccessControlContext privilegedContext)
285     {
286         this.context = context;
287         this.privilegedContext = privilegedContext;
288         this.isPrivileged = true;
289     }
290 
291     /**
292      * Returns this context's context.
293      */
294     ProtectionDomain[] getContext() {
295         return context;
296     }
297 
298     /**
299      * Returns true if this context is privileged.
300      */
301     boolean isPrivileged()
302     {
303         return isPrivileged;
304     }
305 
306     /**
307      * get the assigned combiner from the privileged or inherited context
308      */
309     DomainCombiner getAssignedCombiner() {
310         AccessControlContext acc;
311         if (isPrivileged) {
312             acc = privilegedContext;
313         } else {
314             acc = AccessController.getInheritedAccessControlContext();
315         }
316         if (acc != null) {
317             return acc.combiner;
318         }
319         return null;
320     }
321 
322     /**
323      * Get the {@code DomainCombiner} associated with this
324      * {@code AccessControlContext}.
325      *
326      * <p>
327      *
328      * @return the {@code DomainCombiner} associated with this
329      *          {@code AccessControlContext}, or {@code null}
330      *          if there is none.
331      *
332      * @exception SecurityException if a security manager is installed and
333      *          the caller does not have the "getDomainCombiner"
334      *          {@link SecurityPermission}
335      * @since 1.3
336      */
337     public DomainCombiner getDomainCombiner() {
338 
339         SecurityManager sm = System.getSecurityManager();
340         if (sm != null) {
341             sm.checkPermission(SecurityConstants.GET_COMBINER_PERMISSION);
342         }
343         return getCombiner();
344     }
345 
346     /**
347      * package private for AccessController
348      */
349     DomainCombiner getCombiner() {
350         return combiner;
351     }
352 
353     boolean isAuthorized() {
354         return isAuthorized;
355     }
356 
357     /**
358      * Determines whether the access request indicated by the
359      * specified permission should be allowed or denied, based on
360      * the security policy currently in effect, and the context in
361      * this object. The request is allowed only if every ProtectionDomain
362      * in the context implies the permission. Otherwise the request is
363      * denied.
364      *
365      * <p>
366      * This method quietly returns if the access request
367      * is permitted, or throws a suitable AccessControlException otherwise.
368      *
369      * @param perm the requested permission.
370      *
371      * @exception AccessControlException if the specified permission
372      * is not permitted, based on the current security policy and the
373      * context encapsulated by this object.
374      * @exception NullPointerException if the permission to check for is null.
375      */
376     public void checkPermission(Permission perm)
377         throws AccessControlException
378     {
379         boolean dumpDebug = false;
380 
381         if (perm == null) {
382             throw new NullPointerException("permission can't be null");
383         }
384         if (getDebug() != null) {
385             // If "codebase" is not specified, we dump the info by default.
386             dumpDebug = !Debug.isOn("codebase=");
387             if (!dumpDebug) {
388                 // If "codebase" is specified, only dump if the specified code
389                 // value is in the stack.
390                 for (int i = 0; context != null && i < context.length; i++) {
391                     if (context[i].getCodeSource() != null &&
392                         context[i].getCodeSource().getLocation() != null &&
393                         Debug.isOn("codebase=" + context[i].getCodeSource().getLocation().toString())) {
394                         dumpDebug = true;
395                         break;
396                     }
397                 }
398             }
399 
400             dumpDebug &= !Debug.isOn("permission=") ||
401                 Debug.isOn("permission=" + perm.getClass().getCanonicalName());
402 
403             if (dumpDebug && Debug.isOn("stack")) {
404                 Thread.dumpStack();
405             }
406 
407             if (dumpDebug && Debug.isOn("domain")) {
408                 if (context == null) {
409                     debug.println("domain (context is null)");
410                 } else {
411                     for (int i=0; i< context.length; i++) {
412                         debug.println("domain "+i+" "+context[i]);
413                     }
414                 }
415             }
416         }
417 
418         /*
419          * iterate through the ProtectionDomains in the context.
420          * Stop at the first one that doesn't allow the
421          * requested permission (throwing an exception).
422          *
423          */
424 
425         /* if ctxt is null, all we had on the stack were system domains,
426            or the first domain was a Privileged system domain. This
427            is to make the common case for system code very fast */
428 
429         if (context == null) {
430             checkPermission2(perm);
431             return;
432         }
433 
434         for (int i=0; i< context.length; i++) {
435             if (context[i] != null &&  !context[i].implies(perm)) {
436                 if (dumpDebug) {
437                     debug.println("access denied " + perm);
438                 }
439 
440                 if (Debug.isOn("failure") && debug != null) {
441                     // Want to make sure this is always displayed for failure,
442                     // but do not want to display again if already displayed
443                     // above.
444                     if (!dumpDebug) {
445                         debug.println("access denied " + perm);
446                     }
447                     Thread.dumpStack();
448                     final ProtectionDomain pd = context[i];
449                     final Debug db = debug;
450                     AccessController.doPrivileged (new PrivilegedAction<Void>() {
451                         public Void run() {
452                             db.println("domain that failed "+pd);
453                             return null;
454                         }
455                     });
456                 }
457                 throw new AccessControlException("access denied "+perm, perm);
458             }
459         }
460 
461         // allow if all of them allowed access
462         if (dumpDebug) {
463             debug.println("access allowed "+perm);
464         }
465 
466         checkPermission2(perm);
467     }
468 
469     /*
470      * Check the domains associated with the limited privilege scope.
471      */
472     private void checkPermission2(Permission perm) {
473         if (!isLimited) {
474             return;
475         }
476 
477         /*
478          * Check the doPrivileged() context parameter, if present.
479          */
480         if (privilegedContext != null) {
481             privilegedContext.checkPermission2(perm);
482         }
483 
484         /*
485          * Ignore the limited permissions and parent fields of a wrapper
486          * context since they were already carried down into the unwrapped
487          * context.
488          */
489         if (isWrapped) {
490             return;
491         }
492 
493         /*
494          * Try to match any limited privilege scope.
495          */
496         if (permissions != null) {
497             Class<?> permClass = perm.getClass();
498             for (int i=0; i < permissions.length; i++) {
499                 Permission limit = permissions[i];
500                 if (limit.getClass().equals(permClass) && limit.implies(perm)) {
501                     return;
502                 }
503             }
504         }
505 
506         /*
507          * Check the limited privilege scope up the call stack or the inherited
508          * parent thread call stack of this ACC.
509          */
510         if (parent != null) {
511             /*
512              * As an optimization, if the parent context is the inherited call
513              * stack context from a parent thread then checking the protection
514              * domains of the parent context is redundant since they have
515              * already been merged into the child thread's context by
516              * optimize(). When parent is set to an inherited context this
517              * context was not directly created by a limited scope
518              * doPrivileged() and it does not have its own limited permissions.
519              */
520             if (permissions == null) {
521                 parent.checkPermission2(perm);
522             } else {
523                 parent.checkPermission(perm);
524             }
525         }
526     }
527 
528     /**
529      * Take the stack-based context (this) and combine it with the
530      * privileged or inherited context, if need be. Any limited
531      * privilege scope is flagged regardless of whether the assigned
532      * context comes from an immediately enclosing limited doPrivileged().
533      * The limited privilege scope can indirectly flow from the inherited
534      * parent thread or an assigned context previously captured by getContext().
535      */
536     AccessControlContext optimize() {
537         // the assigned (privileged or inherited) context
538         AccessControlContext acc;
539         DomainCombiner combiner = null;
540         AccessControlContext parent = null;
541         Permission[] permissions = null;
542 
543         if (isPrivileged) {
544             acc = privilegedContext;
545             if (acc != null) {
546                 /*
547                  * If the context is from a limited scope doPrivileged() then
548                  * copy the permissions and parent fields out of the wrapper
549                  * context that was created to hold them.
550                  */
551                 if (acc.isWrapped) {
552                     permissions = acc.permissions;
553                     parent = acc.parent;
554                 }
555             }
556         } else {
557             acc = AccessController.getInheritedAccessControlContext();
558             if (acc != null) {
559                 /*
560                  * If the inherited context is constrained by a limited scope
561                  * doPrivileged() then set it as our parent so we will process
562                  * the non-domain-related state.
563                  */
564                 if (acc.isLimited) {
565                     parent = acc;
566                 }
567             }
568         }
569 
570         // this.context could be null if only system code is on the stack;
571         // in that case, ignore the stack context
572         boolean skipStack = (context == null);
573 
574         // acc.context could be null if only system code was involved;
575         // in that case, ignore the assigned context
576         boolean skipAssigned = (acc == null || acc.context == null);
577         ProtectionDomain[] assigned = (skipAssigned) ? null : acc.context;
578         ProtectionDomain[] pd;
579 
580         // if there is no enclosing limited privilege scope on the stack or
581         // inherited from a parent thread
582         boolean skipLimited = ((acc == null || !acc.isWrapped) && parent == null);
583 
584         if (acc != null && acc.combiner != null) {
585             // let the assigned acc's combiner do its thing
586             if (getDebug() != null) {
587                 debug.println("AccessControlContext invoking the Combiner");
588             }
589 
590             // No need to clone current and assigned.context
591             // combine() will not update them
592             combiner = acc.combiner;
593             pd = combiner.combine(context, assigned);
594         } else {
595             if (skipStack) {
596                 if (skipAssigned) {
597                     calculateFields(acc, parent, permissions);
598                     return this;
599                 } else if (skipLimited) {
600                     return acc;
601                 }
602             } else if (assigned != null) {
603                 if (skipLimited) {
604                     // optimization: if there is a single stack domain and
605                     // that domain is already in the assigned context; no
606                     // need to combine
607                     if (context.length == 1 && context[0] == assigned[0]) {
608                         return acc;
609                     }
610                 }
611             }
612 
613             pd = combine(context, assigned);
614             if (skipLimited && !skipAssigned && pd == assigned) {
615                 return acc;
616             } else if (skipAssigned && pd == context) {
617                 calculateFields(acc, parent, permissions);
618                 return this;
619             }
620         }
621 
622         // Reuse existing ACC
623         this.context = pd;
624         this.combiner = combiner;
625         this.isPrivileged = false;
626 
627         calculateFields(acc, parent, permissions);
628         return this;
629     }
630 
631 
632     /*
633      * Combine the current (stack) and assigned domains.
634      */
635     private static ProtectionDomain[] combine(ProtectionDomain[]current,
636         ProtectionDomain[] assigned) {
637 
638         // current could be null if only system code is on the stack;
639         // in that case, ignore the stack context
640         boolean skipStack = (current == null);
641 
642         // assigned could be null if only system code was involved;
643         // in that case, ignore the assigned context
644         boolean skipAssigned = (assigned == null);
645 
646         int slen = (skipStack) ? 0 : current.length;
647 
648         // optimization: if there is no assigned context and the stack length
649         // is less then or equal to two; there is no reason to compress the
650         // stack context, it already is
651         if (skipAssigned && slen <= 2) {
652             return current;
653         }
654 
655         int n = (skipAssigned) ? 0 : assigned.length;
656 
657         // now we combine both of them, and create a new context
658         ProtectionDomain pd[] = new ProtectionDomain[slen + n];
659 
660         // first copy in the assigned context domains, no need to compress
661         if (!skipAssigned) {
662             System.arraycopy(assigned, 0, pd, 0, n);
663         }
664 
665         // now add the stack context domains, discarding nulls and duplicates
666     outer:
667         for (int i = 0; i < slen; i++) {
668             ProtectionDomain sd = current[i];
669             if (sd != null) {
670                 for (int j = 0; j < n; j++) {
671                     if (sd == pd[j]) {
672                         continue outer;
673                     }
674                 }
675                 pd[n++] = sd;
676             }
677         }
678 
679         // if length isn't equal, we need to shorten the array
680         if (n != pd.length) {
681             // optimization: if we didn't really combine anything
682             if (!skipAssigned && n == assigned.length) {
683                 return assigned;
684             } else if (skipAssigned && n == slen) {
685                 return current;
686             }
687             ProtectionDomain tmp[] = new ProtectionDomain[n];
688             System.arraycopy(pd, 0, tmp, 0, n);
689             pd = tmp;
690         }
691 
692         return pd;
693     }
694 
695 
696     /*
697      * Calculate the additional domains that could potentially be reached via
698      * limited privilege scope. Mark the context as being subject to limited
699      * privilege scope unless the reachable domains (if any) are already
700      * contained in this domain context (in which case any limited
701      * privilege scope checking would be redundant).
702      */
703     private void calculateFields(AccessControlContext assigned,
704         AccessControlContext parent, Permission[] permissions)
705     {
706         ProtectionDomain[] parentLimit = null;
707         ProtectionDomain[] assignedLimit = null;
708         ProtectionDomain[] newLimit;
709 
710         parentLimit = (parent != null)? parent.limitedContext: null;
711         assignedLimit = (assigned != null)? assigned.limitedContext: null;
712         newLimit = combine(parentLimit, assignedLimit);
713         if (newLimit != null) {
714             if (context == null || !containsAllPDs(newLimit, context)) {
715                 this.limitedContext = newLimit;
716                 this.permissions = permissions;
717                 this.parent = parent;
718                 this.isLimited = true;
719             }
720         }
721     }
722 
723 
724     /**
725      * Checks two AccessControlContext objects for equality.
726      * Checks that <i>obj</i> is
727      * an AccessControlContext and has the same set of ProtectionDomains
728      * as this context.
729      * <P>
730      * @param obj the object we are testing for equality with this object.
731      * @return true if <i>obj</i> is an AccessControlContext, and has the
732      * same set of ProtectionDomains as this context, false otherwise.
733      */
734     public boolean equals(Object obj) {
735         if (obj == this)
736             return true;
737 
738         if (! (obj instanceof AccessControlContext))
739             return false;
740 
741         AccessControlContext that = (AccessControlContext) obj;
742 
743         if (!equalContext(that))
744             return false;
745 
746         if (!equalLimitedContext(that))
747             return false;
748 
749         return true;
750     }
751 
752     /*
753      * Compare for equality based on state that is free of limited
754      * privilege complications.
755      */
756     private boolean equalContext(AccessControlContext that) {
757         if (!equalPDs(this.context, that.context))
758             return false;
759 
760         if (this.combiner == null && that.combiner != null)
761             return false;
762 
763         if (this.combiner != null && !this.combiner.equals(that.combiner))
764             return false;
765 
766         return true;
767     }
768 
769     private boolean equalPDs(ProtectionDomain[] a, ProtectionDomain[] b) {
770         if (a == null) {
771             return (b == null);
772         }
773 
774         if (b == null)
775             return false;
776 
777         if (!(containsAllPDs(a, b) && containsAllPDs(b, a)))
778             return false;
779 
780         return true;
781     }
782 
783     /*
784      * Compare for equality based on state that is captured during a
785      * call to AccessController.getContext() when a limited privilege
786      * scope is in effect.
787      */
788     private boolean equalLimitedContext(AccessControlContext that) {
789         if (that == null)
790             return false;
791 
792         /*
793          * If neither instance has limited privilege scope then we're done.
794          */
795         if (!this.isLimited && !that.isLimited)
796             return true;
797 
798         /*
799          * If only one instance has limited privilege scope then we're done.
800          */
801          if (!(this.isLimited && that.isLimited))
802              return false;
803 
804         /*
805          * Wrapped instances should never escape outside the implementation
806          * this class and AccessController so this will probably never happen
807          * but it only makes any sense to compare if they both have the same
808          * isWrapped state.
809          */
810         if ((this.isWrapped && !that.isWrapped) ||
811             (!this.isWrapped && that.isWrapped)) {
812             return false;
813         }
814 
815         if (this.permissions == null && that.permissions != null)
816             return false;
817 
818         if (this.permissions != null && that.permissions == null)
819             return false;
820 
821         if (!(this.containsAllLimits(that) && that.containsAllLimits(this)))
822             return false;
823 
824         /*
825          * Skip through any wrapped contexts.
826          */
827         AccessControlContext thisNextPC = getNextPC(this);
828         AccessControlContext thatNextPC = getNextPC(that);
829 
830         /*
831          * The protection domains and combiner of a privilegedContext are
832          * not relevant because they have already been included in the context
833          * of this instance by optimize() so we only care about any limited
834          * privilege state they may have.
835          */
836         if (thisNextPC == null && thatNextPC != null && thatNextPC.isLimited)
837             return false;
838 
839         if (thisNextPC != null && !thisNextPC.equalLimitedContext(thatNextPC))
840             return false;
841 
842         if (this.parent == null && that.parent != null)
843             return false;
844 
845         if (this.parent != null && !this.parent.equals(that.parent))
846             return false;
847 
848         return true;
849     }
850 
851     /*
852      * Follow the privilegedContext link making our best effort to skip
853      * through any wrapper contexts.
854      */
855     private static AccessControlContext getNextPC(AccessControlContext acc) {
856         while (acc != null && acc.privilegedContext != null) {
857             acc = acc.privilegedContext;
858             if (!acc.isWrapped)
859                 return acc;
860         }
861         return null;
862     }
863 
864     private static boolean containsAllPDs(ProtectionDomain[] thisContext,
865         ProtectionDomain[] thatContext) {
866         boolean match = false;
867 
868         //
869         // ProtectionDomains within an ACC currently cannot be null
870         // and this is enforced by the constructor and the various
871         // optimize methods. However, historically this logic made attempts
872         // to support the notion of a null PD and therefore this logic continues
873         // to support that notion.
874         ProtectionDomain thisPd;
875         for (int i = 0; i < thisContext.length; i++) {
876             match = false;
877             if ((thisPd = thisContext[i]) == null) {
878                 for (int j = 0; (j < thatContext.length) && !match; j++) {
879                     match = (thatContext[j] == null);
880                 }
881             } else {
882                 Class<?> thisPdClass = thisPd.getClass();
883                 ProtectionDomain thatPd;
884                 for (int j = 0; (j < thatContext.length) && !match; j++) {
885                     thatPd = thatContext[j];
886 
887                     // Class check required to avoid PD exposure (4285406)
888                     match = (thatPd != null &&
889                         thisPdClass == thatPd.getClass() && thisPd.equals(thatPd));
890                 }
891             }
892             if (!match) return false;
893         }
894         return match;
895     }
896 
897     private boolean containsAllLimits(AccessControlContext that) {
898         boolean match = false;
899         Permission thisPerm;
900 
901         if (this.permissions == null && that.permissions == null)
902             return true;
903 
904         for (int i = 0; i < this.permissions.length; i++) {
905             Permission limit = this.permissions[i];
906             Class <?> limitClass = limit.getClass();
907             match = false;
908             for (int j = 0; (j < that.permissions.length) && !match; j++) {
909                 Permission perm = that.permissions[j];
910                 match = (limitClass.equals(perm.getClass()) &&
911                     limit.equals(perm));
912             }
913             if (!match) return false;
914         }
915         return match;
916     }
917 
918 
919     /**
920      * Returns the hash code value for this context. The hash code
921      * is computed by exclusive or-ing the hash code of all the protection
922      * domains in the context together.
923      *
924      * @return a hash code value for this context.
925      */
926 
927     public int hashCode() {
928         int hashCode = 0;
929 
930         if (context == null)
931             return hashCode;
932 
933         for (int i =0; i < context.length; i++) {
934             if (context[i] != null)
935                 hashCode ^= context[i].hashCode();
936         }
937 
938         return hashCode;
939     }
940 }