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