- /*
- * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
-
- package javax.security.auth;
-
- import java.security.AccessController;
- import java.security.AccessControlContext;
- import java.security.AllPermission;
- import java.security.Permission;
- import java.security.Permissions;
- import java.security.PermissionCollection;
- import java.security.PrivilegedAction;
- import java.security.ProtectionDomain;
- import java.util.Hashtable;
-
- /**
- * A <code>SubjectDomainCombiner</code> updates the
- * ProtectionDomains affiliated with an <code>AccessControlContext</code>
- * with the relevant Subject-based Permissions configured in the
- * <code>java.security.auth.Policy</code>.
- *
- * <p> If the <code>Security</code> property, <i>cache.auth.policy</i>,
- * either is not set, or if it is set to <i>true</i>, then this
- * <code>SubjectDomainCombiner</code> implementation caches
- * policy information. Otherwise, this <code>SubjectDomainCombiner</code>
- * implementation refrains from performing any caching.
- * The <i>cache.auth.policy</i> property may be set in the Security
- * properties file, located in the file named
- * <JAVA_HOME>/lib/security/java.security, where <JAVA_HOME>
- * refers to the directory where the SDK was installed.
- *
- * @version 1.19, 01/13/00
- */
- public class SubjectDomainCombiner implements java.security.DomainCombiner {
-
- private static Policy policy;
- private Subject subject;
- private Hashtable cachedProtectionDomains = new Hashtable();
- private static boolean checkedCacheProperty = false;
- private static boolean allowCaching = true;
-
- private static final AllPermission ALL_PERMISSION = new AllPermission();
-
- private static final Debug debug = Debug.getInstance("combiner",
- "\t[SubjectDomainCombiner]");
-
- /**
- * Associate the provided <code>Subject</code> with this
- * <code>SubjectDomainCombiner</code>.
- *
- * <p>
- *
- * @param subject the <code>Subject</code> to be associated with
- * with this <code>SubjectDomainCombiner</code>.
- */
- public SubjectDomainCombiner(Subject subject) {
- this.subject = subject;
-
- // XXX must go here -- that way the policy gets initialized
- // before anyone has done a doPrivileged or doAs
- // with a SubjectDomainCombiner. otherwise,
- // you will get into an infinite loop since:
- // 1) combine calls Policy.getPolicy()
- // 2) a security check is invoked
- // 3) SecurityManager calls AccessController.getContext()
- // 4) combine() gets called again
- if (policy == null)
- policy = Policy.getPolicy();
-
- // see if we allow caching of the permissions
- if (!checkedCacheProperty) {
- allowCaching = cachePolicy();
- checkedCacheProperty = true;
- }
-
- }
-
- /**
- * Get the <code>Subject</code> associated with this
- * <code>SubjectDomainCombiner</code>.
- *
- * <p>
- *
- * @return the <code>Subject</code> associated with this
- * <code>SubjectDomainCombiner</code>, or <code>null</code>
- * if no <code>Subject</code> is associated with this
- * <code>SubjectDomainCombiner</code>.
- *
- * @exception SecurityException if the caller does not have permission
- * to get the <code>Subject</code> associated with this
- * <code>SubjectDomainCombiner</code>.
- */
- public Subject getSubject() {
-
- java.lang.SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- sm.checkPermission(new AuthPermission
- ("getSubjectFromDomainCombiner"));
- }
-
- return subject;
- }
-
- /**
- * Update the provided ProtectionDomains with the relevant
- * Subject-based Permissions.
- *
- * <p> For each <code>ProtectionDomain</code> in the
- * <i>currentDomains</i> array, this method retrieves the
- * Subject-based permissions granted in the <code>Policy</code>.
- * To retrieve these permissions, this method invokes the
- * <code>getPermissions</code> method from the <code>Policy</code>,
- * passing it the <code>Subject</code> associated with this
- * <code>SubjectDomainCombiner</code>, along with the
- * <code>CodeSource</code> associated with the respective
- * <code>ProtectionDomain</code>.
- * A new collection of Permissions is created from the union of the
- * retrieved Permissions and the original Permissions granted to that
- * <code>ProtectionDomain</code>. A new
- * <code>ProtectionDomain</code> is then instantiated
- * with this new collection of Permissions, as well as the
- * <code>CodeSource</code> from the original <code>ProtectionDomain</code>.
- * All of the newly instantiated ProtectionDomains are then
- * combined into a new array. The ProtectionDomains from the
- * <i>assignedDomains</i> array are appended to this new array,
- * and the result is returned.
- *
- * <p> Note that optimizations such as the removal of duplicate entries
- * in the array, or the removal of duplicate Permissions in a
- * <code>ProtectionDomain</code> entry may have occurred.
- * Also note that if caching is permitted and the relevant
- * Permissions were already cached, this method does not need to consult
- * the <code>Policy</code> to retrieve the Subject-based permissions.
- * It simply accesses the cached permissions.
- *
- * <p>
- *
- * @param currentDomains the ProtectionDomains associated with the
- * current execution Thread, up to the most recent
- * privileged <code>ProtectionDomain</code>.
- * The ProtectionDomains are are listed in order of execution,
- * with the most recently executing <code>ProtectionDomain</code>
- * residing at the beginning of the array. This parameter may
- * be <code>null</code> if the current execution Thread
- * has no associated ProtectionDomains.<p>
- *
- * @param assignedDomains the ProtectionDomains inherited from the
- * parent Thread, or the ProtectionDomains from the
- * privileged <i>context</i>, if a call to
- * AccessController.doPrivileged(..., <i>context</i>)
- * had occurred This parameter may be <code>null</code>
- * if there were no ProtectionDomains inherited from the
- * parent Thread, or from the privileged <i>context</i>.
- *
- * @return a new array consisting of the updated ProtectionDomains,
- * or <code>null</code>.
- */
- public ProtectionDomain[] combine(ProtectionDomain[] currentDomains,
- ProtectionDomain[] assignedDomains) {
-
- if (currentDomains == null || currentDomains.length == 0)
- return optimize(assignedDomains);
-
- if (debug != null) {
- if (subject == null) {
- debug.println("null subject");
- } else {
- final Subject s = subject;
- AccessController.doPrivileged
- (new java.security.PrivilegedAction() {
- public Object run() {
- debug.println(s.toString());
- return null;
- }
- });
- }
- printInputDomains(currentDomains, assignedDomains);
- }
-
- if (!allowCaching) {
- java.security.AccessController.doPrivileged
- (new PrivilegedAction() {
- public Object run() {
- policy.refresh();
- return null;
- }
- });
- }
-
- // optimize the inputs
- currentDomains = optimize(currentDomains);
- assignedDomains = optimize(assignedDomains);
- if (debug != null) {
- debug.println("after optimize");
- printInputDomains(currentDomains, assignedDomains);
- }
-
- if (currentDomains == null && assignedDomains == null)
- return null;
-
- int cLen = (currentDomains == null ? 0 : currentDomains.length);
- int aLen = (assignedDomains == null ? 0 : assignedDomains.length);
-
- // the ProtectionDomains for the new AccessControlContext
- // that we will return
- ProtectionDomain[] newDomains = new ProtectionDomain[cLen + aLen];
- int newDomainIndex = 0;
-
- // update the permissions for the currentDomains
- for (int i = 0; i < cLen; i++) {
-
- // XXX
- // one of the negatives of moving the PolicyFile implementation
- // to the com.sun package is that we no longer have a
- // SubjectCodeSource implementation to use here
- // (for debugging or as a hash index for caching).
- // we could duplicate SubjectCodeSource, but that's not good either.
-
- java.security.CodeSource cs = currentDomains[i].getCodeSource();
- ProtectionDomain subjectPd = null;
- CacheEntry ce = new CacheEntry(subject, cs);
- if ((subjectPd = (ProtectionDomain)
- cachedProtectionDomains.get(ce)) == null) {
-
- // XXX
- // we must first add the original permissions.
- // that way when we later add the new JAAS permissions,
- // any unresolved JAAS-related permissions will
- // automatically get resolved.
-
- // get the original perms
- Permissions perms = new Permissions();
- java.util.Enumeration e =
- currentDomains[i].getPermissions().elements();
- while (e.hasMoreElements()) {
- Permission newPerm = (Permission)e.nextElement();
- perms.add(newPerm);
- }
-
- // get perms from the policy
- PermissionCollection newPerms = null;
-
- final java.security.CodeSource finalCs = cs;
- final Subject finalS = subject;
- newPerms = (PermissionCollection)
- java.security.AccessController.doPrivileged
- (new PrivilegedAction() {
- public Object run() {
- return policy.getPermissions(finalS, finalCs);
- }
- });
-
- // add the newly granted perms,
- // avoiding duplicates
- e = newPerms.elements();
- while (e.hasMoreElements()) {
- Permission newPerm = (Permission)e.nextElement();
- if (!perms.implies(newPerm))
- perms.add(newPerm);
- }
- subjectPd = new ProtectionDomain(cs, perms);
- }
-
- newDomains[newDomainIndex++] = subjectPd;
- if (allowCaching)
- cachedProtectionDomains.put(ce, subjectPd);
- }
-
- if (debug != null) {
- debug.println("updated current: ");
- if (newDomainIndex == 0) {
- debug.println("\tkept nothing");
- } else {
- for (int j = 0; j < newDomainIndex; j++) {
- debug.println("\tupdated[" + j + "] = " + newDomains[j]);
- }
- }
- }
-
- // now add on the assigned domains
- if (aLen > 0) {
- System.arraycopy(assignedDomains, 0,
- newDomains, newDomainIndex,
- aLen);
- }
-
- // optimize the result
- newDomains = optimize(newDomains);
-
- if (debug != null) {
- if (newDomains == null || newDomains.length == 0) {
- debug.println("returning null");
- } else {
- debug.println("combinedDomains: ");
- for (int i = 0; i < newDomains.length; i++) {
- debug.println("newDomain " + i + ": " +
- newDomains[i].toString());
- }
- }
- }
-
- // return the new ProtectionDomains
- if (newDomains == null || newDomains.length == 0) {
- return null;
- } else {
- return newDomains;
- }
- }
-
- ProtectionDomain[] optimize(ProtectionDomain[] domains) {
-
- if (domains == null)
- return null;
-
- ProtectionDomain[] optimized = new ProtectionDomain[domains.length];
- int num = 0;
- for (int i = 0; i < domains.length; i++) {
-
- // skip System Domains
- if (domains[i] == null)
- continue;
-
- // skip domains with AllPermission
- // XXX
- //
- // if (domains[i].implies(ALL_PERMISSION))
- // continue;
-
- // remove duplicates
- boolean foundIt = false;
- for (int j = 0; j < num; j++) {
- if (optimized[j] == domains[i]) {
- foundIt = true;
- break;
- }
- }
- if (foundIt == false)
- optimized[num++] = domains[i];
- }
-
- // resize the array if necessary
- if (num < domains.length) {
- ProtectionDomain[] downSize = new ProtectionDomain[num];
- System.arraycopy(optimized, 0, downSize, 0, downSize.length);
- optimized = downSize;
- }
-
- return (optimized.length == 0 ? null : optimized);
- }
-
- private boolean cachePolicy() {
- String s = (String)AccessController.doPrivileged
- (new PrivilegedAction() {
- public Object run() {
- return java.security.Security.getProperty
- ("cache.auth.policy");
- }
- });
- if (s != null) {
- Boolean b = new Boolean(s);
- return b.booleanValue();
- }
-
- // cache by default
- return true;
- }
-
- private void printInputDomains(ProtectionDomain[] currentDomains,
- ProtectionDomain[] assignedDomains) {
-
- if (currentDomains == null || currentDomains.length == 0) {
- debug.println("currentDomains null or 0 length");
- } else {
- for (int i = 0; currentDomains != null &&
- i < currentDomains.length; i++) {
- if (currentDomains[i] == null) {
- debug.println("currentDomain " + i + ": SystemDomain");
- } else {
- debug.println("currentDomain " + i + ": " +
- currentDomains[i].toString());
- }
- }
- }
-
- if (assignedDomains == null || assignedDomains.length == 0) {
- debug.println("assignedDomains null or 0 length");
- } else {
- debug.println("assignedDomains = ");
- for (int i = 0; assignedDomains != null &&
- i < assignedDomains.length; i++) {
- if (assignedDomains[i] == null) {
- debug.println("assignedDomain " + i + ": SystemDomain");
- } else {
- debug.println("assignedDomain " + i + ": " +
- assignedDomains[i].toString());
- }
- }
- }
- }
-
- /**
- * It would be nice to use a SubjectCodeSource instead of this class.
- * but since SubjectCodeSource is package private inside of
- * the com package, and we do not want to duplicate it in this package,
- * we will live with having this inner class.
- */
- private class CacheEntry {
- private Subject subject;
- private java.security.CodeSource codesource;
-
- public CacheEntry(Subject s, java.security.CodeSource cs) {
- this.subject = s;
- this.codesource = cs;
- }
-
- public boolean equals(Object o) {
- if (o == null)
- return false;
-
- if (this == o)
- return true;
-
- if (o instanceof CacheEntry) {
- CacheEntry that = (CacheEntry)o;
-
- // compare codesources
- if ((this.codesource == null && that.codesource == null) ||
- (this.codesource != null && that.codesource != null &&
- this.codesource.equals(that.codesource))) {
- // compare subject principals
- if ((this.subject == null && that.subject == null) ||
- (this.subject != null && that.subject != null &&
- this.subject.getPrincipals().containsAll
- (that.subject.getPrincipals()) &&
- that.subject.getPrincipals().containsAll
- (this.subject.getPrincipals()))) {
- return true;
- }
- }
- }
- return false;
- }
-
- public int hashCode() {
- if (codesource != null) {
- return codesource.hashCode();
- } else {
- if (subject == null)
- return 0;
- else
- return subject.hashCode();
- }
- }
- }
- }