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 }