Save This Page
Home » openjdk-7 » java » security » [javadoc | source]
    1   /*
    2    * Copyright 1997-2008 Sun Microsystems, Inc.  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.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any 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    * An AccessControlContext is used to make system resource access decisions
   35    * based on the context it encapsulates.
   36    *
   37    * <p>More specifically, it encapsulates a context and
   38    * has a single method, <code>checkPermission</code>,
   39    * that is equivalent to the <code>checkPermission</code> method
   40    * in the AccessController class, with one difference: The AccessControlContext
   41    * <code>checkPermission</code> method makes access decisions based on the
   42    * context it encapsulates,
   43    * rather than that of the current execution thread.
   44    *
   45    * <p>Thus, the purpose of AccessControlContext is for those situations where
   46    * a security check that should be made within a given context
   47    * actually needs to be done from within a
   48    * <i>different</i> context (for example, from within a worker thread).
   49    *
   50    * <p> An AccessControlContext is created by calling the
   51    * <code>AccessController.getContext</code> method.
   52    * The <code>getContext</code> method takes a "snapshot"
   53    * of the current calling context, and places
   54    * it in an AccessControlContext object, which it returns. A sample call is
   55    * the following:
   56    *
   57    * <pre>
   58    *   AccessControlContext acc = AccessController.getContext()
   59    * </pre>
   60    *
   61    * <p>
   62    * Code within a different context can subsequently call the
   63    * <code>checkPermission</code> method on the
   64    * previously-saved AccessControlContext object. A sample call is the
   65    * following:
   66    *
   67    * <pre>
   68    *   acc.checkPermission(permission)
   69    * </pre>
   70    *
   71    * @see AccessController
   72    *
   73    * @author Roland Schemers
   74    */
   75   
   76   public final class AccessControlContext {
   77   
   78       private ProtectionDomain context[];
   79       private boolean isPrivileged;
   80   
   81       // Note: This field is directly used by the virtual machine
   82       // native codes. Don't touch it.
   83       private AccessControlContext privilegedContext;
   84   
   85       private DomainCombiner combiner = null;
   86   
   87       private static boolean debugInit = false;
   88       private static Debug debug = null;
   89   
   90       static Debug getDebug()
   91       {
   92           if (debugInit)
   93               return debug;
   94           else {
   95               if (Policy.isSet()) {
   96                   debug = Debug.getInstance("access");
   97                   debugInit = true;
   98               }
   99               return debug;
  100           }
  101       }
  102   
  103       /**
  104        * Create an AccessControlContext with the given array of ProtectionDomains.
  105        * Context must not be null. Duplicate domains will be removed from the
  106        * context.
  107        *
  108        * @param context the ProtectionDomains associated with this context.
  109        * The non-duplicate domains are copied from the array. Subsequent
  110        * changes to the array will not affect this AccessControlContext.
  111        * @throws NullPointerException if <code>context</code> is <code>null</code>
  112        */
  113       public AccessControlContext(ProtectionDomain context[])
  114       {
  115           if (context.length == 0) {
  116               this.context = null;
  117           } else if (context.length == 1) {
  118               if (context[0] != null) {
  119                   this.context = context.clone();
  120               } else {
  121                   this.context = null;
  122               }
  123           } else {
  124               List<ProtectionDomain> v = new ArrayList<ProtectionDomain>(context.length);
  125               for (int i =0; i< context.length; i++) {
  126                   if ((context[i] != null) &&  (!v.contains(context[i])))
  127                       v.add(context[i]);
  128               }
  129               if (!v.isEmpty()) {
  130                   this.context = new ProtectionDomain[v.size()];
  131                   this.context = v.toArray(this.context);
  132               }
  133           }
  134       }
  135   
  136       /**
  137        * Create a new <code>AccessControlContext</code> with the given
  138        * <code>AccessControlContext</code> and <code>DomainCombiner</code>.
  139        * This constructor associates the provided
  140        * <code>DomainCombiner</code> with the provided
  141        * <code>AccessControlContext</code>.
  142        *
  143        * <p>
  144        *
  145        * @param acc the <code>AccessControlContext</code> associated
  146        *          with the provided <code>DomainCombiner</code>.
  147        *
  148        * @param combiner the <code>DomainCombiner</code> to be associated
  149        *          with the provided <code>AccessControlContext</code>.
  150        *
  151        * @exception NullPointerException if the provided
  152        *          <code>context</code> is <code>null</code>.
  153        *
  154        * @exception SecurityException if a security manager is installed and the
  155        *          caller does not have the "createAccessControlContext"
  156        *          {@link SecurityPermission}
  157        * @since 1.3
  158        */
  159       public AccessControlContext(AccessControlContext acc,
  160                                   DomainCombiner combiner) {
  161   
  162           SecurityManager sm = System.getSecurityManager();
  163           if (sm != null) {
  164               sm.checkPermission(SecurityConstants.CREATE_ACC_PERMISSION);
  165           }
  166   
  167           this.context = acc.context;
  168   
  169           // we do not need to run the combine method on the
  170           // provided ACC.  it was already "combined" when the
  171           // context was originally retrieved.
  172           //
  173           // at this point in time, we simply throw away the old
  174           // combiner and use the newly provided one.
  175           this.combiner = combiner;
  176       }
  177   
  178       /**
  179        * package private for AccessController
  180        */
  181       AccessControlContext(ProtectionDomain context[], DomainCombiner combiner) {
  182           if (context != null) {
  183               this.context = context.clone();
  184           }
  185           this.combiner = combiner;
  186       }
  187   
  188       /**
  189        * package private constructor for AccessController.getContext()
  190        */
  191   
  192       AccessControlContext(ProtectionDomain context[],
  193                                    boolean isPrivileged)
  194       {
  195           this.context = context;
  196           this.isPrivileged = isPrivileged;
  197       }
  198   
  199       /**
  200        * Returns true if this context is privileged.
  201        */
  202       boolean isPrivileged()
  203       {
  204           return isPrivileged;
  205       }
  206   
  207       /**
  208        * get the assigned combiner from the privileged or inherited context
  209        */
  210       DomainCombiner getAssignedCombiner() {
  211           AccessControlContext acc;
  212           if (isPrivileged) {
  213               acc = privilegedContext;
  214           } else {
  215               acc = AccessController.getInheritedAccessControlContext();
  216           }
  217           if (acc != null) {
  218               return acc.combiner;
  219           }
  220           return null;
  221       }
  222   
  223       /**
  224        * Get the <code>DomainCombiner</code> associated with this
  225        * <code>AccessControlContext</code>.
  226        *
  227        * <p>
  228        *
  229        * @return the <code>DomainCombiner</code> associated with this
  230        *          <code>AccessControlContext</code>, or <code>null</code>
  231        *          if there is none.
  232        *
  233        * @exception SecurityException if a security manager is installed and
  234        *          the caller does not have the "getDomainCombiner"
  235        *          {@link SecurityPermission}
  236        * @since 1.3
  237        */
  238       public DomainCombiner getDomainCombiner() {
  239   
  240           SecurityManager sm = System.getSecurityManager();
  241           if (sm != null) {
  242               sm.checkPermission(SecurityConstants.GET_COMBINER_PERMISSION);
  243           }
  244           return combiner;
  245       }
  246   
  247       /**
  248        * Determines whether the access request indicated by the
  249        * specified permission should be allowed or denied, based on
  250        * the security policy currently in effect, and the context in
  251        * this object. The request is allowed only if every ProtectionDomain
  252        * in the context implies the permission. Otherwise the request is
  253        * denied.
  254        *
  255        * <p>
  256        * This method quietly returns if the access request
  257        * is permitted, or throws a suitable AccessControlException otherwise.
  258        *
  259        * @param perm the requested permission.
  260        *
  261        * @exception AccessControlException if the specified permission
  262        * is not permitted, based on the current security policy and the
  263        * context encapsulated by this object.
  264        * @exception NullPointerException if the permission to check for is null.
  265        */
  266       public void checkPermission(Permission perm)
  267           throws AccessControlException
  268       {
  269           boolean dumpDebug = false;
  270   
  271           if (perm == null) {
  272               throw new NullPointerException("permission can't be null");
  273           }
  274           if (getDebug() != null) {
  275               // If "codebase" is not specified, we dump the info by default.
  276               dumpDebug = !Debug.isOn("codebase=");
  277               if (!dumpDebug) {
  278                   // If "codebase" is specified, only dump if the specified code
  279                   // value is in the stack.
  280                   for (int i = 0; context != null && i < context.length; i++) {
  281                       if (context[i].getCodeSource() != null &&
  282                           context[i].getCodeSource().getLocation() != null &&
  283                           Debug.isOn("codebase=" + context[i].getCodeSource().getLocation().toString())) {
  284                           dumpDebug = true;
  285                           break;
  286                       }
  287                   }
  288               }
  289   
  290               dumpDebug &= !Debug.isOn("permission=") ||
  291                   Debug.isOn("permission=" + perm.getClass().getCanonicalName());
  292   
  293               if (dumpDebug && Debug.isOn("stack")) {
  294                   Thread.currentThread().dumpStack();
  295               }
  296   
  297               if (dumpDebug && Debug.isOn("domain")) {
  298                   if (context == null) {
  299                       debug.println("domain (context is null)");
  300                   } else {
  301                       for (int i=0; i< context.length; i++) {
  302                           debug.println("domain "+i+" "+context[i]);
  303                       }
  304                   }
  305               }
  306           }
  307   
  308           /*
  309            * iterate through the ProtectionDomains in the context.
  310            * Stop at the first one that doesn't allow the
  311            * requested permission (throwing an exception).
  312            *
  313            */
  314   
  315           /* if ctxt is null, all we had on the stack were system domains,
  316              or the first domain was a Privileged system domain. This
  317              is to make the common case for system code very fast */
  318   
  319           if (context == null)
  320               return;
  321   
  322           for (int i=0; i< context.length; i++) {
  323               if (context[i] != null &&  !context[i].implies(perm)) {
  324                   if (dumpDebug) {
  325                       debug.println("access denied " + perm);
  326                   }
  327   
  328                   if (Debug.isOn("failure") && debug != null) {
  329                       // Want to make sure this is always displayed for failure,
  330                       // but do not want to display again if already displayed
  331                       // above.
  332                       if (!dumpDebug) {
  333                           debug.println("access denied " + perm);
  334                       }
  335                       Thread.currentThread().dumpStack();
  336                       final ProtectionDomain pd = context[i];
  337                       final Debug db = debug;
  338                       AccessController.doPrivileged (new PrivilegedAction<Void>() {
  339                           public Void run() {
  340                               db.println("domain that failed "+pd);
  341                               return null;
  342                           }
  343                       });
  344                   }
  345                   throw new AccessControlException("access denied "+perm, perm);
  346               }
  347           }
  348   
  349           // allow if all of them allowed access
  350           if (dumpDebug) {
  351               debug.println("access allowed "+perm);
  352           }
  353   
  354           return;
  355       }
  356   
  357       /**
  358        * Take the stack-based context (this) and combine it with the
  359        * privileged or inherited context, if need be.
  360        */
  361       AccessControlContext optimize() {
  362           // the assigned (privileged or inherited) context
  363           AccessControlContext acc;
  364           if (isPrivileged) {
  365               acc = privilegedContext;
  366           } else {
  367               acc = AccessController.getInheritedAccessControlContext();
  368           }
  369   
  370           // this.context could be null if only system code is on the stack;
  371           // in that case, ignore the stack context
  372           boolean skipStack = (context == null);
  373   
  374           // acc.context could be null if only system code was involved;
  375           // in that case, ignore the assigned context
  376           boolean skipAssigned = (acc == null || acc.context == null);
  377   
  378           if (acc != null && acc.combiner != null) {
  379               // let the assigned acc's combiner do its thing
  380               return goCombiner(context, acc);
  381           }
  382   
  383           // optimization: if neither have contexts; return acc if possible
  384           // rather than this, because acc might have a combiner
  385           if (skipAssigned && skipStack) {
  386               return this;
  387           }
  388   
  389           // optimization: if there is no stack context; there is no reason
  390           // to compress the assigned context, it already is compressed
  391           if (skipStack) {
  392               return acc;
  393           }
  394   
  395           int slen = context.length;
  396   
  397           // optimization: if there is no assigned context and the stack length
  398           // is less then or equal to two; there is no reason to compress the
  399           // stack context, it already is
  400           if (skipAssigned && slen <= 2) {
  401               return this;
  402           }
  403   
  404           // optimization: if there is a single stack domain and that domain
  405           // is already in the assigned context; no need to combine
  406           if ((slen == 1) && (context[0] == acc.context[0])) {
  407               return acc;
  408           }
  409   
  410           int n = (skipAssigned) ? 0 : acc.context.length;
  411   
  412           // now we combine both of them, and create a new context
  413           ProtectionDomain pd[] = new ProtectionDomain[slen + n];
  414   
  415           // first copy in the assigned context domains, no need to compress
  416           if (!skipAssigned) {
  417               System.arraycopy(acc.context, 0, pd, 0, n);
  418           }
  419   
  420           // now add the stack context domains, discarding nulls and duplicates
  421       outer:
  422           for (int i = 0; i < context.length; i++) {
  423               ProtectionDomain sd = context[i];
  424               if (sd != null) {
  425                   for (int j = 0; j < n; j++) {
  426                       if (sd == pd[j]) {
  427                           continue outer;
  428                       }
  429                   }
  430                   pd[n++] = sd;
  431               }
  432           }
  433   
  434           // if length isn't equal, we need to shorten the array
  435           if (n != pd.length) {
  436               // optimization: if we didn't really combine anything
  437               if (!skipAssigned && n == acc.context.length) {
  438                   return acc;
  439               } else if (skipAssigned && n == slen) {
  440                   return this;
  441               }
  442               ProtectionDomain tmp[] = new ProtectionDomain[n];
  443               System.arraycopy(pd, 0, tmp, 0, n);
  444               pd = tmp;
  445           }
  446   
  447           //      return new AccessControlContext(pd, false);
  448   
  449           // Reuse existing ACC
  450   
  451           this.context = pd;
  452           this.combiner = null;
  453           this.isPrivileged = false;
  454   
  455           return this;
  456       }
  457   
  458       private AccessControlContext goCombiner(ProtectionDomain[] current,
  459                                           AccessControlContext assigned) {
  460   
  461           // the assigned ACC's combiner is not null --
  462           // let the combiner do its thing
  463   
  464           // XXX we could add optimizations to 'current' here ...
  465   
  466           if (getDebug() != null) {
  467               debug.println("AccessControlContext invoking the Combiner");
  468           }
  469   
  470           // No need to clone current and assigned.context
  471           // combine() will not update them
  472           ProtectionDomain[] combinedPds = assigned.combiner.combine(
  473               current, assigned.context);
  474   
  475           // return new AccessControlContext(combinedPds, assigned.combiner);
  476   
  477           // Reuse existing ACC
  478           this.context = combinedPds;
  479           this.combiner = assigned.combiner;
  480           this.isPrivileged = false;
  481   
  482           return this;
  483       }
  484   
  485       /**
  486        * Checks two AccessControlContext objects for equality.
  487        * Checks that <i>obj</i> is
  488        * an AccessControlContext and has the same set of ProtectionDomains
  489        * as this context.
  490        * <P>
  491        * @param obj the object we are testing for equality with this object.
  492        * @return true if <i>obj</i> is an AccessControlContext, and has the
  493        * same set of ProtectionDomains as this context, false otherwise.
  494        */
  495       public boolean equals(Object obj) {
  496           if (obj == this)
  497               return true;
  498   
  499           if (! (obj instanceof AccessControlContext))
  500               return false;
  501   
  502           AccessControlContext that = (AccessControlContext) obj;
  503   
  504   
  505           if (context == null) {
  506               return (that.context == null);
  507           }
  508   
  509           if (that.context == null)
  510               return false;
  511   
  512           if (!(this.containsAllPDs(that) && that.containsAllPDs(this)))
  513               return false;
  514   
  515           if (this.combiner == null)
  516               return (that.combiner == null);
  517   
  518           if (that.combiner == null)
  519               return false;
  520   
  521           if (!this.combiner.equals(that.combiner))
  522               return false;
  523   
  524           return true;
  525       }
  526   
  527       private boolean containsAllPDs(AccessControlContext that) {
  528           boolean match = false;
  529           //
  530           // ProtectionDomains within an ACC currently cannot be null
  531           // and this is enforced by the constructor and the various
  532           // optimize methods. However, historically this logic made attempts
  533           // to support the notion of a null PD and therefore this logic continues
  534           // to support that notion.
  535           ProtectionDomain thisPd;
  536           for (int i = 0; i < context.length; i++) {
  537               match = false;
  538               if ((thisPd = context[i]) == null) {
  539                   for (int j = 0; (j < that.context.length) && !match; j++) {
  540                       match = (that.context[j] == null);
  541                   }
  542               } else {
  543                   Class thisPdClass = thisPd.getClass();
  544                   ProtectionDomain thatPd;
  545                   for (int j = 0; (j < that.context.length) && !match; j++) {
  546                       thatPd = that.context[j];
  547   
  548                       // Class check required to avoid PD exposure (4285406)
  549                       match = (thatPd != null &&
  550                           thisPdClass == thatPd.getClass() && thisPd.equals(thatPd));
  551                   }
  552               }
  553               if (!match) return false;
  554           }
  555           return match;
  556       }
  557       /**
  558        * Returns the hash code value for this context. The hash code
  559        * is computed by exclusive or-ing the hash code of all the protection
  560        * domains in the context together.
  561        *
  562        * @return a hash code value for this context.
  563        */
  564   
  565       public int hashCode() {
  566           int hashCode = 0;
  567   
  568           if (context == null)
  569               return hashCode;
  570   
  571           for (int i =0; i < context.length; i++) {
  572               if (context[i] != null)
  573                   hashCode ^= context[i].hashCode();
  574           }
  575           return hashCode;
  576       }
  577   }

Save This Page
Home » openjdk-7 » java » security » [javadoc | source]