Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/java/security/AccessControlContext.java
38829 views
/*1* Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.security;2627import java.util.ArrayList;28import java.util.List;29import sun.security.util.Debug;30import sun.security.util.SecurityConstants;313233/**34* An AccessControlContext is used to make system resource access decisions35* based on the context it encapsulates.36*37* <p>More specifically, it encapsulates a context and38* has a single method, {@code checkPermission},39* that is equivalent to the {@code checkPermission} method40* in the AccessController class, with one difference: The AccessControlContext41* {@code checkPermission} method makes access decisions based on the42* context it encapsulates,43* rather than that of the current execution thread.44*45* <p>Thus, the purpose of AccessControlContext is for those situations where46* a security check that should be made within a given context47* actually needs to be done from within a48* <i>different</i> context (for example, from within a worker thread).49*50* <p> An AccessControlContext is created by calling the51* {@code AccessController.getContext} method.52* The {@code getContext} method takes a "snapshot"53* of the current calling context, and places54* it in an AccessControlContext object, which it returns. A sample call is55* the following:56*57* <pre>58* AccessControlContext acc = AccessController.getContext()59* </pre>60*61* <p>62* Code within a different context can subsequently call the63* {@code checkPermission} method on the64* previously-saved AccessControlContext object. A sample call is the65* following:66*67* <pre>68* acc.checkPermission(permission)69* </pre>70*71* @see AccessController72*73* @author Roland Schemers74*/7576public final class AccessControlContext {7778private ProtectionDomain context[];79// isPrivileged and isAuthorized are referenced by the VM - do not remove80// or change their names81private boolean isPrivileged;82private boolean isAuthorized = false;8384// Note: This field is directly used by the virtual machine85// native codes. Don't touch it.86private AccessControlContext privilegedContext;8788private DomainCombiner combiner = null;8990// limited privilege scope91private Permission permissions[];92private AccessControlContext parent;93private boolean isWrapped;9495// is constrained by limited privilege scope?96private boolean isLimited;97private ProtectionDomain limitedContext[];9899private static boolean debugInit = false;100private static Debug debug = null;101102static Debug getDebug()103{104if (debugInit)105return debug;106else {107if (Policy.isSet()) {108debug = Debug.getInstance("access");109debugInit = true;110}111return debug;112}113}114115/**116* Create an AccessControlContext with the given array of ProtectionDomains.117* Context must not be null. Duplicate domains will be removed from the118* context.119*120* @param context the ProtectionDomains associated with this context.121* The non-duplicate domains are copied from the array. Subsequent122* changes to the array will not affect this AccessControlContext.123* @throws NullPointerException if {@code context} is {@code null}124*/125public AccessControlContext(ProtectionDomain context[])126{127if (context.length == 0) {128this.context = null;129} else if (context.length == 1) {130if (context[0] != null) {131this.context = context.clone();132} else {133this.context = null;134}135} else {136List<ProtectionDomain> v = new ArrayList<>(context.length);137for (int i =0; i< context.length; i++) {138if ((context[i] != null) && (!v.contains(context[i])))139v.add(context[i]);140}141if (!v.isEmpty()) {142this.context = new ProtectionDomain[v.size()];143this.context = v.toArray(this.context);144}145}146}147148/**149* Create a new {@code AccessControlContext} with the given150* {@code AccessControlContext} and {@code DomainCombiner}.151* This constructor associates the provided152* {@code DomainCombiner} with the provided153* {@code AccessControlContext}.154*155* <p>156*157* @param acc the {@code AccessControlContext} associated158* with the provided {@code DomainCombiner}.159*160* @param combiner the {@code DomainCombiner} to be associated161* with the provided {@code AccessControlContext}.162*163* @exception NullPointerException if the provided164* {@code context} is {@code null}.165*166* @exception SecurityException if a security manager is installed and the167* caller does not have the "createAccessControlContext"168* {@link SecurityPermission}169* @since 1.3170*/171public AccessControlContext(AccessControlContext acc,172DomainCombiner combiner) {173174this(acc, combiner, false);175}176177/**178* package private to allow calls from ProtectionDomain without performing179* the security check for {@linkplain SecurityConstants.CREATE_ACC_PERMISSION}180* permission181*/182AccessControlContext(AccessControlContext acc,183DomainCombiner combiner,184boolean preauthorized) {185if (!preauthorized) {186SecurityManager sm = System.getSecurityManager();187if (sm != null) {188sm.checkPermission(SecurityConstants.CREATE_ACC_PERMISSION);189this.isAuthorized = true;190}191} else {192this.isAuthorized = true;193}194195this.context = acc.context;196197// we do not need to run the combine method on the198// provided ACC. it was already "combined" when the199// context was originally retrieved.200//201// at this point in time, we simply throw away the old202// combiner and use the newly provided one.203this.combiner = combiner;204}205206/**207* package private for AccessController208*209* This "argument wrapper" context will be passed as the actual context210* parameter on an internal doPrivileged() call used in the implementation.211*/212AccessControlContext(ProtectionDomain caller, DomainCombiner combiner,213AccessControlContext parent, AccessControlContext context,214Permission[] perms)215{216/*217* Combine the domains from the doPrivileged() context into our218* wrapper context, if necessary.219*/220ProtectionDomain[] callerPDs = null;221if (caller != null) {222callerPDs = new ProtectionDomain[] { caller };223}224if (context != null) {225if (combiner != null) {226this.context = combiner.combine(callerPDs, context.context);227} else {228this.context = combine(callerPDs, context.context);229}230} else {231/*232* Call combiner even if there is seemingly nothing to combine.233*/234if (combiner != null) {235this.context = combiner.combine(callerPDs, null);236} else {237this.context = combine(callerPDs, null);238}239}240this.combiner = combiner;241242Permission[] tmp = null;243if (perms != null) {244tmp = new Permission[perms.length];245for (int i=0; i < perms.length; i++) {246if (perms[i] == null) {247throw new NullPointerException("permission can't be null");248}249250/*251* An AllPermission argument is equivalent to calling252* doPrivileged() without any limit permissions.253*/254if (perms[i].getClass() == AllPermission.class) {255parent = null;256}257tmp[i] = perms[i];258}259}260261/*262* For a doPrivileged() with limited privilege scope, initialize263* the relevant fields.264*265* The limitedContext field contains the union of all domains which266* are enclosed by this limited privilege scope. In other words,267* it contains all of the domains which could potentially be checked268* if none of the limiting permissions implied a requested permission.269*/270if (parent != null) {271this.limitedContext = combine(parent.context, parent.limitedContext);272this.isLimited = true;273this.isWrapped = true;274this.permissions = tmp;275this.parent = parent;276this.privilegedContext = context; // used in checkPermission2()277}278this.isAuthorized = true;279}280281282/**283* package private constructor for AccessController.getContext()284*/285286AccessControlContext(ProtectionDomain context[],287boolean isPrivileged)288{289this.context = context;290this.isPrivileged = isPrivileged;291this.isAuthorized = true;292}293294/**295* Constructor for JavaSecurityAccess.doIntersectionPrivilege()296*/297AccessControlContext(ProtectionDomain[] context,298AccessControlContext privilegedContext)299{300this.context = context;301this.privilegedContext = privilegedContext;302this.isPrivileged = true;303}304305/**306* Returns this context's context.307*/308ProtectionDomain[] getContext() {309return context;310}311312/**313* Returns true if this context is privileged.314*/315boolean isPrivileged()316{317return isPrivileged;318}319320/**321* get the assigned combiner from the privileged or inherited context322*/323DomainCombiner getAssignedCombiner() {324AccessControlContext acc;325if (isPrivileged) {326acc = privilegedContext;327} else {328acc = AccessController.getInheritedAccessControlContext();329}330if (acc != null) {331return acc.combiner;332}333return null;334}335336/**337* Get the {@code DomainCombiner} associated with this338* {@code AccessControlContext}.339*340* <p>341*342* @return the {@code DomainCombiner} associated with this343* {@code AccessControlContext}, or {@code null}344* if there is none.345*346* @exception SecurityException if a security manager is installed and347* the caller does not have the "getDomainCombiner"348* {@link SecurityPermission}349* @since 1.3350*/351public DomainCombiner getDomainCombiner() {352353SecurityManager sm = System.getSecurityManager();354if (sm != null) {355sm.checkPermission(SecurityConstants.GET_COMBINER_PERMISSION);356}357return getCombiner();358}359360/**361* package private for AccessController362*/363DomainCombiner getCombiner() {364return combiner;365}366367boolean isAuthorized() {368return isAuthorized;369}370371/**372* Determines whether the access request indicated by the373* specified permission should be allowed or denied, based on374* the security policy currently in effect, and the context in375* this object. The request is allowed only if every ProtectionDomain376* in the context implies the permission. Otherwise the request is377* denied.378*379* <p>380* This method quietly returns if the access request381* is permitted, or throws a suitable AccessControlException otherwise.382*383* @param perm the requested permission.384*385* @exception AccessControlException if the specified permission386* is not permitted, based on the current security policy and the387* context encapsulated by this object.388* @exception NullPointerException if the permission to check for is null.389*/390public void checkPermission(Permission perm)391throws AccessControlException392{393boolean dumpDebug = false;394395if (perm == null) {396throw new NullPointerException("permission can't be null");397}398if (getDebug() != null) {399// If "codebase" is not specified, we dump the info by default.400dumpDebug = !Debug.isOn("codebase=");401if (!dumpDebug) {402// If "codebase" is specified, only dump if the specified code403// value is in the stack.404for (int i = 0; context != null && i < context.length; i++) {405if (context[i].getCodeSource() != null &&406context[i].getCodeSource().getLocation() != null &&407Debug.isOn("codebase=" + context[i].getCodeSource().getLocation().toString())) {408dumpDebug = true;409break;410}411}412}413414dumpDebug &= !Debug.isOn("permission=") ||415Debug.isOn("permission=" + perm.getClass().getCanonicalName());416417if (dumpDebug && Debug.isOn("stack")) {418Thread.dumpStack();419}420421if (dumpDebug && Debug.isOn("domain")) {422if (context == null) {423debug.println("domain (context is null)");424} else {425for (int i=0; i< context.length; i++) {426debug.println("domain "+i+" "+context[i]);427}428}429}430}431432/*433* iterate through the ProtectionDomains in the context.434* Stop at the first one that doesn't allow the435* requested permission (throwing an exception).436*437*/438439/* if ctxt is null, all we had on the stack were system domains,440or the first domain was a Privileged system domain. This441is to make the common case for system code very fast */442443if (context == null) {444checkPermission2(perm);445return;446}447448for (int i=0; i< context.length; i++) {449if (context[i] != null && !context[i].implies(perm)) {450if (dumpDebug) {451debug.println("access denied " + perm);452}453454if (Debug.isOn("failure") && debug != null) {455// Want to make sure this is always displayed for failure,456// but do not want to display again if already displayed457// above.458if (!dumpDebug) {459debug.println("access denied " + perm);460}461Thread.dumpStack();462final ProtectionDomain pd = context[i];463final Debug db = debug;464AccessController.doPrivileged (new PrivilegedAction<Void>() {465public Void run() {466db.println("domain that failed "+pd);467return null;468}469});470}471throw new AccessControlException("access denied "+perm, perm);472}473}474475// allow if all of them allowed access476if (dumpDebug) {477debug.println("access allowed "+perm);478}479480checkPermission2(perm);481}482483/*484* Check the domains associated with the limited privilege scope.485*/486private void checkPermission2(Permission perm) {487if (!isLimited) {488return;489}490491/*492* Check the doPrivileged() context parameter, if present.493*/494if (privilegedContext != null) {495privilegedContext.checkPermission2(perm);496}497498/*499* Ignore the limited permissions and parent fields of a wrapper500* context since they were already carried down into the unwrapped501* context.502*/503if (isWrapped) {504return;505}506507/*508* Try to match any limited privilege scope.509*/510if (permissions != null) {511Class<?> permClass = perm.getClass();512for (int i=0; i < permissions.length; i++) {513Permission limit = permissions[i];514if (limit.getClass().equals(permClass) && limit.implies(perm)) {515return;516}517}518}519520/*521* Check the limited privilege scope up the call stack or the inherited522* parent thread call stack of this ACC.523*/524if (parent != null) {525/*526* As an optimization, if the parent context is the inherited call527* stack context from a parent thread then checking the protection528* domains of the parent context is redundant since they have529* already been merged into the child thread's context by530* optimize(). When parent is set to an inherited context this531* context was not directly created by a limited scope532* doPrivileged() and it does not have its own limited permissions.533*/534if (permissions == null) {535parent.checkPermission2(perm);536} else {537parent.checkPermission(perm);538}539}540}541542/**543* Take the stack-based context (this) and combine it with the544* privileged or inherited context, if need be. Any limited545* privilege scope is flagged regardless of whether the assigned546* context comes from an immediately enclosing limited doPrivileged().547* The limited privilege scope can indirectly flow from the inherited548* parent thread or an assigned context previously captured by getContext().549*/550AccessControlContext optimize() {551// the assigned (privileged or inherited) context552AccessControlContext acc;553DomainCombiner combiner = null;554AccessControlContext parent = null;555Permission[] permissions = null;556557if (isPrivileged) {558acc = privilegedContext;559if (acc != null) {560/*561* If the context is from a limited scope doPrivileged() then562* copy the permissions and parent fields out of the wrapper563* context that was created to hold them.564*/565if (acc.isWrapped) {566permissions = acc.permissions;567parent = acc.parent;568}569}570} else {571acc = AccessController.getInheritedAccessControlContext();572if (acc != null) {573/*574* If the inherited context is constrained by a limited scope575* doPrivileged() then set it as our parent so we will process576* the non-domain-related state.577*/578if (acc.isLimited) {579parent = acc;580}581}582}583584// this.context could be null if only system code is on the stack;585// in that case, ignore the stack context586boolean skipStack = (context == null);587588// acc.context could be null if only system code was involved;589// in that case, ignore the assigned context590boolean skipAssigned = (acc == null || acc.context == null);591ProtectionDomain[] assigned = (skipAssigned) ? null : acc.context;592ProtectionDomain[] pd;593594// if there is no enclosing limited privilege scope on the stack or595// inherited from a parent thread596boolean skipLimited = ((acc == null || !acc.isWrapped) && parent == null);597598if (acc != null && acc.combiner != null) {599// let the assigned acc's combiner do its thing600if (getDebug() != null) {601debug.println("AccessControlContext invoking the Combiner");602}603604// No need to clone current and assigned.context605// combine() will not update them606combiner = acc.combiner;607pd = combiner.combine(context, assigned);608} else {609if (skipStack) {610if (skipAssigned) {611calculateFields(acc, parent, permissions);612return this;613} else if (skipLimited) {614return acc;615}616} else if (assigned != null) {617if (skipLimited) {618// optimization: if there is a single stack domain and619// that domain is already in the assigned context; no620// need to combine621if (context.length == 1 && context[0] == assigned[0]) {622return acc;623}624}625}626627pd = combine(context, assigned);628if (skipLimited && !skipAssigned && pd == assigned) {629return acc;630} else if (skipAssigned && pd == context) {631calculateFields(acc, parent, permissions);632return this;633}634}635636// Reuse existing ACC637this.context = pd;638this.combiner = combiner;639this.isPrivileged = false;640641calculateFields(acc, parent, permissions);642return this;643}644645646/*647* Combine the current (stack) and assigned domains.648*/649private static ProtectionDomain[] combine(ProtectionDomain[]current,650ProtectionDomain[] assigned) {651652// current could be null if only system code is on the stack;653// in that case, ignore the stack context654boolean skipStack = (current == null);655656// assigned could be null if only system code was involved;657// in that case, ignore the assigned context658boolean skipAssigned = (assigned == null);659660int slen = (skipStack) ? 0 : current.length;661662// optimization: if there is no assigned context and the stack length663// is less then or equal to two; there is no reason to compress the664// stack context, it already is665if (skipAssigned && slen <= 2) {666return current;667}668669int n = (skipAssigned) ? 0 : assigned.length;670671// now we combine both of them, and create a new context672ProtectionDomain pd[] = new ProtectionDomain[slen + n];673674// first copy in the assigned context domains, no need to compress675if (!skipAssigned) {676System.arraycopy(assigned, 0, pd, 0, n);677}678679// now add the stack context domains, discarding nulls and duplicates680outer:681for (int i = 0; i < slen; i++) {682ProtectionDomain sd = current[i];683if (sd != null) {684for (int j = 0; j < n; j++) {685if (sd == pd[j]) {686continue outer;687}688}689pd[n++] = sd;690}691}692693// if length isn't equal, we need to shorten the array694if (n != pd.length) {695// optimization: if we didn't really combine anything696if (!skipAssigned && n == assigned.length) {697return assigned;698} else if (skipAssigned && n == slen) {699return current;700}701ProtectionDomain tmp[] = new ProtectionDomain[n];702System.arraycopy(pd, 0, tmp, 0, n);703pd = tmp;704}705706return pd;707}708709710/*711* Calculate the additional domains that could potentially be reached via712* limited privilege scope. Mark the context as being subject to limited713* privilege scope unless the reachable domains (if any) are already714* contained in this domain context (in which case any limited715* privilege scope checking would be redundant).716*/717private void calculateFields(AccessControlContext assigned,718AccessControlContext parent, Permission[] permissions)719{720ProtectionDomain[] parentLimit = null;721ProtectionDomain[] assignedLimit = null;722ProtectionDomain[] newLimit;723724parentLimit = (parent != null)? parent.limitedContext: null;725assignedLimit = (assigned != null)? assigned.limitedContext: null;726newLimit = combine(parentLimit, assignedLimit);727if (newLimit != null) {728if (context == null || !containsAllPDs(newLimit, context)) {729this.limitedContext = newLimit;730this.permissions = permissions;731this.parent = parent;732this.isLimited = true;733}734}735}736737738/**739* Checks two AccessControlContext objects for equality.740* Checks that <i>obj</i> is741* an AccessControlContext and has the same set of ProtectionDomains742* as this context.743* <P>744* @param obj the object we are testing for equality with this object.745* @return true if <i>obj</i> is an AccessControlContext, and has the746* same set of ProtectionDomains as this context, false otherwise.747*/748public boolean equals(Object obj) {749if (obj == this)750return true;751752if (! (obj instanceof AccessControlContext))753return false;754755AccessControlContext that = (AccessControlContext) obj;756757if (!equalContext(that))758return false;759760if (!equalLimitedContext(that))761return false;762763return true;764}765766/*767* Compare for equality based on state that is free of limited768* privilege complications.769*/770private boolean equalContext(AccessControlContext that) {771if (!equalPDs(this.context, that.context))772return false;773774if (this.combiner == null && that.combiner != null)775return false;776777if (this.combiner != null && !this.combiner.equals(that.combiner))778return false;779780return true;781}782783private boolean equalPDs(ProtectionDomain[] a, ProtectionDomain[] b) {784if (a == null) {785return (b == null);786}787788if (b == null)789return false;790791if (!(containsAllPDs(a, b) && containsAllPDs(b, a)))792return false;793794return true;795}796797/*798* Compare for equality based on state that is captured during a799* call to AccessController.getContext() when a limited privilege800* scope is in effect.801*/802private boolean equalLimitedContext(AccessControlContext that) {803if (that == null)804return false;805806/*807* If neither instance has limited privilege scope then we're done.808*/809if (!this.isLimited && !that.isLimited)810return true;811812/*813* If only one instance has limited privilege scope then we're done.814*/815if (!(this.isLimited && that.isLimited))816return false;817818/*819* Wrapped instances should never escape outside the implementation820* this class and AccessController so this will probably never happen821* but it only makes any sense to compare if they both have the same822* isWrapped state.823*/824if ((this.isWrapped && !that.isWrapped) ||825(!this.isWrapped && that.isWrapped)) {826return false;827}828829if (this.permissions == null && that.permissions != null)830return false;831832if (this.permissions != null && that.permissions == null)833return false;834835if (!(this.containsAllLimits(that) && that.containsAllLimits(this)))836return false;837838/*839* Skip through any wrapped contexts.840*/841AccessControlContext thisNextPC = getNextPC(this);842AccessControlContext thatNextPC = getNextPC(that);843844/*845* The protection domains and combiner of a privilegedContext are846* not relevant because they have already been included in the context847* of this instance by optimize() so we only care about any limited848* privilege state they may have.849*/850if (thisNextPC == null && thatNextPC != null && thatNextPC.isLimited)851return false;852853if (thisNextPC != null && !thisNextPC.equalLimitedContext(thatNextPC))854return false;855856if (this.parent == null && that.parent != null)857return false;858859if (this.parent != null && !this.parent.equals(that.parent))860return false;861862return true;863}864865/*866* Follow the privilegedContext link making our best effort to skip867* through any wrapper contexts.868*/869private static AccessControlContext getNextPC(AccessControlContext acc) {870while (acc != null && acc.privilegedContext != null) {871acc = acc.privilegedContext;872if (!acc.isWrapped)873return acc;874}875return null;876}877878private static boolean containsAllPDs(ProtectionDomain[] thisContext,879ProtectionDomain[] thatContext) {880boolean match = false;881882//883// ProtectionDomains within an ACC currently cannot be null884// and this is enforced by the constructor and the various885// optimize methods. However, historically this logic made attempts886// to support the notion of a null PD and therefore this logic continues887// to support that notion.888ProtectionDomain thisPd;889for (int i = 0; i < thisContext.length; i++) {890match = false;891if ((thisPd = thisContext[i]) == null) {892for (int j = 0; (j < thatContext.length) && !match; j++) {893match = (thatContext[j] == null);894}895} else {896Class<?> thisPdClass = thisPd.getClass();897ProtectionDomain thatPd;898for (int j = 0; (j < thatContext.length) && !match; j++) {899thatPd = thatContext[j];900901// Class check required to avoid PD exposure (4285406)902match = (thatPd != null &&903thisPdClass == thatPd.getClass() && thisPd.equals(thatPd));904}905}906if (!match) return false;907}908return match;909}910911private boolean containsAllLimits(AccessControlContext that) {912boolean match = false;913Permission thisPerm;914915if (this.permissions == null && that.permissions == null)916return true;917918for (int i = 0; i < this.permissions.length; i++) {919Permission limit = this.permissions[i];920Class <?> limitClass = limit.getClass();921match = false;922for (int j = 0; (j < that.permissions.length) && !match; j++) {923Permission perm = that.permissions[j];924match = (limitClass.equals(perm.getClass()) &&925limit.equals(perm));926}927if (!match) return false;928}929return match;930}931932933/**934* Returns the hash code value for this context. The hash code935* is computed by exclusive or-ing the hash code of all the protection936* domains in the context together.937*938* @return a hash code value for this context.939*/940941public int hashCode() {942int hashCode = 0;943944if (context == null)945return hashCode;946947for (int i =0; i < context.length; i++) {948if (context[i] != null)949hashCode ^= context[i].hashCode();950}951952return hashCode;953}954}955956957