      1 /*
      2  * Copyright (c) 1997, 2019, 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
     31 import sun.security.util.Debug;
     32 import sun.security.util.FilePermCompat;
     33 import sun.security.util.SecurityConstants;
     34
     35
     36 /**
     37  * An AccessControlContext is used to make system resource access decisions
     38  * based on the context it encapsulates.
     39  *
     40  * <p>More specifically, it encapsulates a context and
     41  * has a single method, {@code checkPermission},
     42  * that is equivalent to the {@code checkPermission} method
     43  * in the AccessController class, with one difference: The AccessControlContext
     44  * {@code checkPermission} method makes access decisions based on the
     45  * context it encapsulates,
     46  * rather than that of the current execution thread.
     47  *
     48  * <p>Thus, the purpose of AccessControlContext is for those situations where
     49  * a security check that should be made within a given context
     50  * actually needs to be done from within a
     51  * <i>different</i> context (for example, from within a worker thread).
     52  *
     53  * <p> An AccessControlContext is created by calling the
     54  * {@code AccessController.getContext} method.
     55  * The {@code getContext} method takes a "snapshot"
     56  * of the current calling context, and places
     57  * it in an AccessControlContext object, which it returns. A sample call is
     58  * the following:
     59  *
     60  * <pre>
     61  *   AccessControlContext acc = AccessController.getContext()
     62  * </pre>
     63  *
     64  * <p>
     65  * Code within a different context can subsequently call the
     66  * {@code checkPermission} method on the
     67  * previously-saved AccessControlContext object. A sample call is the
     68  * following:
     69  *
     70  * <pre>
     71  *   acc.checkPermission(permission)
     72  * </pre>
     73  *
     74  * @see AccessController
     75  *
     76  * @author Roland Schemers
     77  * @since 1.2

     78  */
     79

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