/* * @(#)Subject.java 1.123 04/05/05 * * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package javax.security.auth; import java.util.*; import java.io.*; import java.lang.reflect.*; import java.text.MessageFormat; import java.security.AccessController; import java.security.AccessControlContext; import java.security.DomainCombiner; import java.security.Permission; import java.security.PermissionCollection; import java.security.Principal; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; import java.security.PrivilegedActionException; import java.security.ProtectionDomain; import sun.security.util.ResourcesMgr; import sun.security.util.SecurityConstants; /** *

A Subject represents a grouping of related information * for a single entity, such as a person. * Such information includes the Subject's identities as well as * its security-related attributes * (passwords and cryptographic keys, for example). * *

Subjects may potentially have multiple identities. * Each identity is represented as a Principal * within the Subject. Principals simply bind names to a * Subject. For example, a Subject that happens * to be a person, Alice, might have two Principals: * one which binds "Alice Bar", the name on her driver license, * to the Subject, and another which binds, * "999-99-9999", the number on her student identification card, * to the Subject. Both Principals refer to the same * Subject even though each has a different name. * *

A Subject may also own security-related attributes, * which are referred to as credentials. * Sensitive credentials that require special protection, such as * private cryptographic keys, are stored within a private credential * Set. Credentials intended to be shared, such as * public key certificates or Kerberos server tickets are stored * within a public credential Set. Different permissions * are required to access and modify the different credential Sets. * *

To retrieve all the Principals associated with a Subject, * invoke the getPrincipals method. To retrieve * all the public or private credentials belonging to a Subject, * invoke the getPublicCredentials method or * getPrivateCredentials method, respectively. * To modify the returned Set of Principals and credentials, * use the methods defined in the Set class. * For example: *

 *	Subject subject;
 *	Principal principal;
 *	Object credential;
 *
 *	// add a Principal and credential to the Subject
 *	subject.getPrincipals().add(principal);
 *	subject.getPublicCredentials().add(credential);
 * 
* *

This Subject class implements Serializable. * While the Principals associated with the Subject are serialized, * the credentials associated with the Subject are not. * Note that the java.security.Principal class * does not implement Serializable. Therefore all concrete * Principal implementations associated with Subjects * must implement Serializable. * * @version 1.123, 05/05/04 * @see java.security.Principal * @see java.security.DomainCombiner */ public final class Subject implements java.io.Serializable { private static final long serialVersionUID = -8308522755600156056L; /** * A Set that provides a view of all of this * Subject's Principals * *

* * @serial Each element in this set is a * java.security.Principal. * The set is a Subject.SecureSet. */ Set principals; /** * Sets that provide a view of all of this * Subject's Credentials */ transient Set pubCredentials; transient Set privCredentials; /** * Whether this Subject is read-only * * @serial */ private volatile boolean readOnly = false; private static final int PRINCIPAL_SET = 1; private static final int PUB_CREDENTIAL_SET = 2; private static final int PRIV_CREDENTIAL_SET = 3; /** * Create an instance of a Subject * with an empty Set of Principals and empty * Sets of public and private credentials. * *

The newly constructed Sets check whether this Subject * has been set read-only before permitting subsequent modifications. * The newly created Sets also prevent illegal modifications * by ensuring that callers have sufficient permissions. * *

To modify the Principals Set, the caller must have * AuthPermission("modifyPrincipals"). * To modify the public credential Set, the caller must have * AuthPermission("modifyPublicCredentials"). * To modify the private credential Set, the caller must have * AuthPermission("modifyPrivateCredentials"). */ public Subject() { this.principals = Collections.synchronizedSet (new SecureSet(this, PRINCIPAL_SET)); this.pubCredentials = Collections.synchronizedSet (new SecureSet(this, PUB_CREDENTIAL_SET)); this.privCredentials = Collections.synchronizedSet (new SecureSet(this, PRIV_CREDENTIAL_SET)); } /** * Create an instance of a Subject with * Principals and credentials. * *

The Principals and credentials from the specified Sets * are copied into newly constructed Sets. * These newly created Sets check whether this Subject * has been set read-only before permitting subsequent modifications. * The newly created Sets also prevent illegal modifications * by ensuring that callers have sufficient permissions. * *

To modify the Principals Set, the caller must have * AuthPermission("modifyPrincipals"). * To modify the public credential Set, the caller must have * AuthPermission("modifyPublicCredentials"). * To modify the private credential Set, the caller must have * AuthPermission("modifyPrivateCredentials"). *

* * @param readOnly true if the Subject is to be read-only, * and false otherwise.

* * @param principals the Set of Principals * to be associated with this Subject.

* * @param pubCredentials the Set of public credentials * to be associated with this Subject.

* * @param privCredentials the Set of private credentials * to be associated with this Subject. * * @exception NullPointerException if the specified * principals, pubCredentials, * or privCredentials are null. */ public Subject(boolean readOnly, Set principals, Set pubCredentials, Set privCredentials) { if (principals == null || pubCredentials == null || privCredentials == null) throw new NullPointerException (ResourcesMgr.getString("invalid null input(s)")); this.principals = Collections.synchronizedSet(new SecureSet (this, PRINCIPAL_SET, principals)); this.pubCredentials = Collections.synchronizedSet(new SecureSet (this, PUB_CREDENTIAL_SET, pubCredentials)); this.privCredentials = Collections.synchronizedSet(new SecureSet (this, PRIV_CREDENTIAL_SET, privCredentials)); this.readOnly = readOnly; } /** * Set this Subject to be read-only. * *

Modifications (additions and removals) to this Subject's * Principal Set and * credential Sets will be disallowed. * The destroy operation on this Subject's credentials will * still be permitted. * *

Subsequent attempts to modify the Subject's Principal * and credential Sets will result in an * IllegalStateException being thrown. * Also, once a Subject is read-only, * it can not be reset to being writable again. * *

* * @exception SecurityException if the caller does not have permission * to set this Subject to be read-only. */ public void setReadOnly() { java.lang.SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new AuthPermission("setReadOnly")); } this.readOnly = true; } /** * Query whether this Subject is read-only. * *

* * @return true if this Subject is read-only, false otherwise. */ public boolean isReadOnly() { return this.readOnly; } /** * Get the Subject associated with the provided * AccessControlContext. * *

The AccessControlContext may contain many * Subjects (from nested doAs calls). * In this situation, the most recent Subject associated * with the AccessControlContext is returned. * *

* * @param acc the AccessControlContext from which to retrieve * the Subject. * * @return the Subject associated with the provided * AccessControlContext, or null * if no Subject is associated * with the provided AccessControlContext. * * @exception SecurityException if the caller does not have permission * to get the Subject.

* * @exception NullPointerException if the provided * AccessControlContext is null. */ public static Subject getSubject(final AccessControlContext acc) { java.lang.SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new AuthPermission("getSubject")); } if (acc == null) { throw new NullPointerException(ResourcesMgr.getString ("invalid null AccessControlContext provided")); } // return the Subject from the DomainCombiner of the provided context return (Subject)AccessController.doPrivileged (new java.security.PrivilegedAction() { public Object run() { DomainCombiner dc = acc.getDomainCombiner(); if (!(dc instanceof SubjectDomainCombiner)) return null; SubjectDomainCombiner sdc = (SubjectDomainCombiner)dc; return sdc.getSubject(); } }); } /** * Perform work as a particular Subject. * *

This method first retrieves the current Thread's * AccessControlContext via * AccessController.getContext, * and then instantiates a new AccessControlContext * using the retrieved context along with a new * SubjectDomainCombiner (constructed using * the provided Subject). * Finally, this method invokes AccessController.doPrivileged, * passing it the provided PrivilegedAction, * as well as the newly constructed AccessControlContext. * *

* * @param subject the Subject that the specified * action will run as. This parameter * may be null.

* * @param action the code to be run as the specified * Subject.

* * @return the Object returned by the PrivilegedAction's * run method. * * @exception NullPointerException if the PrivilegedAction * is null.

* * @exception SecurityException if the caller does not have permission * to invoke this method. */ public static Object doAs(final Subject subject, final java.security.PrivilegedAction action) { java.lang.SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(SecurityConstants.DO_AS_PERMISSION); } if (action == null) throw new NullPointerException (ResourcesMgr.getString("invalid null action provided")); // set up the new Subject-based AccessControlContext // for doPrivileged final AccessControlContext currentAcc = AccessController.getContext(); // call doPrivileged and push this new context on the stack return java.security.AccessController.doPrivileged (action, createContext(subject, currentAcc)); } /** * Perform work as a particular Subject. * *

This method first retrieves the current Thread's * AccessControlContext via * AccessController.getContext, * and then instantiates a new AccessControlContext * using the retrieved context along with a new * SubjectDomainCombiner (constructed using * the provided Subject). * Finally, this method invokes AccessController.doPrivileged, * passing it the provided PrivilegedExceptionAction, * as well as the newly constructed AccessControlContext. * *

* * @param subject the Subject that the specified * action will run as. This parameter * may be null.

* * @param action the code to be run as the specified * Subject.

* * @return the Object returned by the * PrivilegedExceptionAction's run method. * * @exception PrivilegedActionException if the * PrivilegedExceptionAction.run * method throws a checked exception.

* * @exception NullPointerException if the specified * PrivilegedExceptionAction is * null.

* * @exception SecurityException if the caller does not have permission * to invoke this method. */ public static Object doAs(final Subject subject, final java.security.PrivilegedExceptionAction action) throws java.security.PrivilegedActionException { java.lang.SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(SecurityConstants.DO_AS_PERMISSION); } if (action == null) throw new NullPointerException (ResourcesMgr.getString("invalid null action provided")); // set up the new Subject-based AccessControlContext for doPrivileged final AccessControlContext currentAcc = AccessController.getContext(); // call doPrivileged and push this new context on the stack return java.security.AccessController.doPrivileged (action, createContext(subject, currentAcc)); } /** * Perform privileged work as a particular Subject. * *

This method behaves exactly as Subject.doAs, * except that instead of retrieving the current Thread's * AccessControlContext, it uses the provided * AccessControlContext. If the provided * AccessControlContext is null, * this method instantiates a new AccessControlContext * with an empty collection of ProtectionDomains. * *

* * @param subject the Subject that the specified * action will run as. This parameter * may be null.

* * @param action the code to be run as the specified * Subject.

* * @param acc the AccessControlContext to be tied to the * specified subject and action.

* * @return the Object returned by the PrivilegedAction's * run method. * * @exception NullPointerException if the PrivilegedAction * is null.

* * @exception SecurityException if the caller does not have permission * to invoke this method. */ public static Object doAsPrivileged(final Subject subject, final java.security.PrivilegedAction action, final java.security.AccessControlContext acc) { java.lang.SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(SecurityConstants.DO_AS_PRIVILEGED_PERMISSION); } if (action == null) throw new NullPointerException (ResourcesMgr.getString("invalid null action provided")); // set up the new Subject-based AccessControlContext // for doPrivileged final AccessControlContext callerAcc = (acc == null ? new AccessControlContext(new ProtectionDomain[0]) : acc); // call doPrivileged and push this new context on the stack return java.security.AccessController.doPrivileged (action, createContext(subject, callerAcc)); } /** * Perform privileged work as a particular Subject. * *

This method behaves exactly as Subject.doAs, * except that instead of retrieving the current Thread's * AccessControlContext, it uses the provided * AccessControlContext. If the provided * AccessControlContext is null, * this method instantiates a new AccessControlContext * with an empty collection of ProtectionDomains. * *

* * @param subject the Subject that the specified * action will run as. This parameter * may be null.

* * @param action the code to be run as the specified * Subject.

* * @param acc the AccessControlContext to be tied to the * specified subject and action.

* * @return the Object returned by the * PrivilegedExceptionAction's run method. * * @exception PrivilegedActionException if the * PrivilegedExceptionAction.run * method throws a checked exception.

* * @exception NullPointerException if the specified * PrivilegedExceptionAction is * null.

* * @exception SecurityException if the caller does not have permission * to invoke this method. */ public static Object doAsPrivileged(final Subject subject, final java.security.PrivilegedExceptionAction action, final java.security.AccessControlContext acc) throws java.security.PrivilegedActionException { java.lang.SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(SecurityConstants.DO_AS_PRIVILEGED_PERMISSION); } if (action == null) throw new NullPointerException (ResourcesMgr.getString("invalid null action provided")); // set up the new Subject-based AccessControlContext for doPrivileged final AccessControlContext callerAcc = (acc == null ? new AccessControlContext(new ProtectionDomain[0]) : acc); // call doPrivileged and push this new context on the stack return java.security.AccessController.doPrivileged (action, createContext(subject, callerAcc)); } private static AccessControlContext createContext(final Subject subject, final AccessControlContext acc) { return (AccessControlContext) java.security.AccessController.doPrivileged (new java.security.PrivilegedAction() { public Object run() { if (subject == null) return new AccessControlContext(acc, null); else return new AccessControlContext (acc, new SubjectDomainCombiner(subject)); } }); } /** * Return the Set of Principals associated with this * Subject. Each Principal represents * an identity for this Subject. * *

The returned Set is backed by this Subject's * internal Principal Set. Any modification * to the returned Set affects the internal * Principal Set as well. * *

* * @return The Set of Principals associated with this * Subject. */ public Set getPrincipals() { // always return an empty Set instead of null // so LoginModules can add to the Set if necessary return principals; } /** * Return a Set of Principals associated with this * Subject that are instances or subclasses of the specified * Class. * *

The returned Set is not backed by this Subject's * internal Principal Set. A new * Set is created and returned for each method invocation. * Modifications to the returned Set * will not affect the internal Principal Set. * *

* * @param c the returned Set of Principals will all be * instances of this class. * * @return a Set of Principals that are instances of the * specified Class. * * @exception NullPointerException if the specified Class * is null. */ public Set getPrincipals(Class c) { if (c == null) throw new NullPointerException (ResourcesMgr.getString("invalid null Class provided")); // always return an empty Set instead of null // so LoginModules can add to the Set if necessary return new ClassSet(PRINCIPAL_SET, c); } /** * Return the Set of public credentials held by this * Subject. * *

The returned Set is backed by this Subject's * internal public Credential Set. Any modification * to the returned Set affects the internal public * Credential Set as well. * *

* * @return A Set of public credentials held by this * Subject. */ public Set getPublicCredentials() { // always return an empty Set instead of null // so LoginModules can add to the Set if necessary return pubCredentials; } /** * Return the Set of private credentials held by this * Subject. * *

The returned Set is backed by this Subject's * internal private Credential Set. Any modification * to the returned Set affects the internal private * Credential Set as well. * *

A caller requires permissions to access the Credentials * in the returned Set, or to modify the * Set itself. A SecurityException * is thrown if the caller does not have the proper permissions. * *

While iterating through the Set, * a SecurityException is thrown * if the caller does not have permission to access a * particular Credential. The Iterator * is nevertheless advanced to next element in the Set. * *

* * @return A Set of private credentials held by this * Subject. */ public Set getPrivateCredentials() { // XXX // we do not need a security check for // AuthPermission(getPrivateCredentials) // because we already restrict access to private credentials // via the PrivateCredentialPermission. all the extra AuthPermission // would do is protect the set operations themselves // (like size()), which don't seem security-sensitive. // always return an empty Set instead of null // so LoginModules can add to the Set if necessary return privCredentials; } /** * Return a Set of public credentials associated with this * Subject that are instances or subclasses of the specified * Class. * *

The returned Set is not backed by this Subject's * internal public Credential Set. A new * Set is created and returned for each method invocation. * Modifications to the returned Set * will not affect the internal public Credential Set. * *

* * @param c the returned Set of public credentials will all be * instances of this class. * * @return a Set of public credentials that are instances * of the specified Class. * * @exception NullPointerException if the specified Class * is null. */ public Set getPublicCredentials(Class c) { if (c == null) throw new NullPointerException (ResourcesMgr.getString("invalid null Class provided")); // always return an empty Set instead of null // so LoginModules can add to the Set if necessary return new ClassSet(PUB_CREDENTIAL_SET, c); } /** * Return a Set of private credentials associated with this * Subject that are instances or subclasses of the specified * Class. * *

The caller must have permission to access all of the * requested Credentials, or a SecurityException * will be thrown. * *

The returned Set is not backed by this Subject's * internal private Credential Set. A new * Set is created and returned for each method invocation. * Modifications to the returned Set * will not affect the internal private Credential Set. * *

* * @param c the returned Set of private credentials will all be * instances of this class. * * @return a Set of private credentials that are instances * of the specified Class. * * @exception NullPointerException if the specified Class * is null. */ public Set getPrivateCredentials(Class c) { // XXX // we do not need a security check for // AuthPermission(getPrivateCredentials) // because we already restrict access to private credentials // via the PrivateCredentialPermission. all the extra AuthPermission // would do is protect the set operations themselves // (like size()), which don't seem security-sensitive. if (c == null) throw new NullPointerException (ResourcesMgr.getString("invalid null Class provided")); // always return an empty Set instead of null // so LoginModules can add to the Set if necessary return new ClassSet(PRIV_CREDENTIAL_SET, c); } /** * Compares the specified Object with this Subject * for equality. Returns true if the given object is also a Subject * and the two Subject instances are equivalent. * More formally, two Subject instances are * equal if their Principal and Credential * Sets are equal. * *

* * @param o Object to be compared for equality with this * Subject. * * @return true if the specified Object is equal to this * Subject. * * @exception SecurityException if the caller does not have permission * to access the private credentials for this Subject, * or if the caller does not have permission to access the * private credentials for the provided Subject. */ public boolean equals(Object o) { if (o == null) return false; if (this == o) return true; if (o instanceof Subject) { final Subject that = (Subject)o; // check the principal and credential sets Set thatPrincipals; synchronized(that.principals) { // avoid deadlock from dual locks thatPrincipals = new HashSet(that.principals); } if (!principals.equals(thatPrincipals)) { return false; } Set thatPubCredentials; synchronized(that.pubCredentials) { // avoid deadlock from dual locks thatPubCredentials = new HashSet(that.pubCredentials); } if (!pubCredentials.equals(thatPubCredentials)) { return false; } Set thatPrivCredentials; synchronized(that.privCredentials) { // avoid deadlock from dual locks thatPrivCredentials = new HashSet(that.privCredentials); } if (!privCredentials.equals(thatPrivCredentials)) { return false; } return true; } return false; } /** * Return the String representation of this Subject. * *

* * @return the String representation of this Subject. */ public String toString() { return toString(true); } /** * package private convenience method to print out the Subject * without firing off a security check when trying to access * the Private Credentials */ String toString(boolean includePrivateCredentials) { String s = new String(ResourcesMgr.getString("Subject:\n")); String suffix = new String(); synchronized(principals) { Iterator pI = principals.iterator(); while (pI.hasNext()) { Principal p = (Principal)pI.next(); suffix = suffix + ResourcesMgr.getString("\tPrincipal: ") + p.toString() + ResourcesMgr.getString("\n"); } } synchronized(pubCredentials) { Iterator pI = pubCredentials.iterator(); while (pI.hasNext()) { Object o = pI.next(); suffix = suffix + ResourcesMgr.getString("\tPublic Credential: ") + o.toString() + ResourcesMgr.getString("\n"); } } if (includePrivateCredentials) { synchronized(privCredentials) { Iterator pI = privCredentials.iterator(); while (pI.hasNext()) { try { Object o = pI.next(); suffix += ResourcesMgr.getString ("\tPrivate Credential: ") + o.toString() + ResourcesMgr.getString("\n"); } catch (SecurityException se) { suffix += ResourcesMgr.getString ("\tPrivate Credential inaccessible\n"); break; } } } } return s + suffix; } /** * Returns a hashcode for this Subject. * *

* * @return a hashcode for this Subject. * * @exception SecurityException if the caller does not have permission * to access this Subject's private credentials. */ public int hashCode() { /** * The hashcode is derived exclusive or-ing the * hashcodes of this Subject's Principals and credentials. * * If a particular credential was destroyed * (credential.hashCode() throws an * IllegalStateException), * the hashcode for that credential is derived via: * credential.getClass().toString().hashCode(). */ int hashCode = 0; synchronized(principals) { Iterator pIterator = principals.iterator(); while (pIterator.hasNext()) { Principal p = (Principal)pIterator.next(); hashCode ^= p.hashCode(); } } synchronized(pubCredentials) { Iterator pubCIterator = pubCredentials.iterator(); while (pubCIterator.hasNext()) { hashCode ^= getCredHashCode(pubCIterator.next()); } } return hashCode; } /** * get a credential's hashcode */ private int getCredHashCode(Object o) { try { return o.hashCode(); } catch (IllegalStateException ise) { return o.getClass().toString().hashCode(); } } /** * Writes this object out to a stream (i.e., serializes it). */ private void writeObject(java.io.ObjectOutputStream oos) throws java.io.IOException { synchronized(principals) { oos.defaultWriteObject(); } } /** * Reads this object from a stream (i.e., deserializes it) */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); // The Credential Set is not serialized, but we do not // want the default deserialization routine to set it to null. this.pubCredentials = Collections.synchronizedSet (new SecureSet(this, PUB_CREDENTIAL_SET)); this.privCredentials = Collections.synchronizedSet (new SecureSet(this, PRIV_CREDENTIAL_SET)); } /** * Prevent modifications unless caller has permission. * * @serial include */ private static class SecureSet extends AbstractSet implements java.io.Serializable { private static final long serialVersionUID = 7911754171111800359L; /** * @serialField this$0 Subject The outer Subject instance. * @serialField elements LinkedList The elements in this set. */ private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("this$0", Subject.class), new ObjectStreamField("elements", LinkedList.class), new ObjectStreamField("which", int.class) }; Subject subject; LinkedList elements; /** * @serial An integer identifying the type of objects contained * in this set. If which == 1, * this is a Principal set and all the elements are * of type java.security.Principal. * If which == 2, this is a public credential * set and all the elements are of type Object. * If which == 3, this is a private credential * set and all the elements are of type Object. */ private int which; SecureSet(Subject subject, int which) { this.subject = subject; this.which = which; this.elements = new LinkedList(); } SecureSet(Subject subject, int which, Set set) { this.subject = subject; this.which = which; this.elements = new LinkedList(set); } public int size() { return elements.size(); } public Iterator iterator() { final LinkedList list = elements; return new Iterator() { ListIterator i = list.listIterator(0); public boolean hasNext() {return i.hasNext();} public Object next() { if (which != Subject.PRIV_CREDENTIAL_SET) { return i.next(); } SecurityManager sm = System.getSecurityManager(); if (sm != null) { try { sm.checkPermission(new PrivateCredentialPermission (list.get(i.nextIndex()).getClass().getName(), subject.getPrincipals())); } catch (SecurityException se) { i.next(); throw (se); } } return i.next(); } public void remove() { if (subject.isReadOnly()) { throw new IllegalStateException(ResourcesMgr.getString ("Subject is read-only")); } java.lang.SecurityManager sm = System.getSecurityManager(); if (sm != null) { switch (which) { case Subject.PRINCIPAL_SET: sm.checkPermission(new AuthPermission ("modifyPrincipals")); break; case Subject.PUB_CREDENTIAL_SET: sm.checkPermission(new AuthPermission ("modifyPublicCredentials")); break; default: sm.checkPermission(new AuthPermission ("modifyPrivateCredentials")); break; } } i.remove(); } }; } public boolean add(Object o) { if (subject.isReadOnly()) { throw new IllegalStateException (ResourcesMgr.getString("Subject is read-only")); } java.lang.SecurityManager sm = System.getSecurityManager(); if (sm != null) { switch (which) { case Subject.PRINCIPAL_SET: sm.checkPermission (new AuthPermission("modifyPrincipals")); break; case Subject.PUB_CREDENTIAL_SET: sm.checkPermission (new AuthPermission("modifyPublicCredentials")); break; default: sm.checkPermission (new AuthPermission("modifyPrivateCredentials")); break; } } switch (which) { case Subject.PRINCIPAL_SET: if (!(o instanceof Principal)) { throw new SecurityException(ResourcesMgr.getString ("attempting to add an object which is not an " + "instance of java.security.Principal to a " + "Subject's Principal Set")); } break; default: // ok to add Objects of any kind to credential sets break; } // check for duplicates if (!elements.contains(o)) return elements.add(o); else return false; } public boolean remove(Object o) { final Iterator e = iterator(); while (e.hasNext()) { Object next; if (which != Subject.PRIV_CREDENTIAL_SET) { next = e.next(); } else { next = (Object)java.security.AccessController.doPrivileged (new java.security.PrivilegedAction() { public Object run() { return e.next(); } }); } if (next == null) { if (o == null) { e.remove(); return true; } } else if (next.equals(o)) { e.remove(); return true; } } return false; } public boolean contains(Object o) { final Iterator e = iterator(); while (e.hasNext()) { Object next; if (which != Subject.PRIV_CREDENTIAL_SET) { next = e.next(); } else { // For private credentials: // If the caller does not have read permission for // for o.getClass(), we throw a SecurityException. // Otherwise we check the private cred set to see whether // it contains the Object SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new PrivateCredentialPermission (o.getClass().getName(), subject.getPrincipals())); } next = (Object)java.security.AccessController.doPrivileged (new java.security.PrivilegedAction() { public Object run() { return e.next(); } }); } if (next == null) { if (o == null) { return true; } } else if (next.equals(o)) { return true; } } return false; } public boolean removeAll(Collection c) { boolean modified = false; final Iterator e = iterator(); while (e.hasNext()) { Object next; if (which != Subject.PRIV_CREDENTIAL_SET) { next = e.next(); } else { next = (Object)java.security.AccessController.doPrivileged (new java.security.PrivilegedAction() { public Object run() { return e.next(); } }); } Iterator ce = c.iterator(); while (ce.hasNext()) { Object o = ce.next(); if (next == null) { if (o == null) { e.remove(); modified = true; break; } } else if (next.equals(o)) { e.remove(); modified = true; break; } } } return modified; } public boolean retainAll(Collection c) { boolean modified = false; boolean retain = false; final Iterator e = iterator(); while (e.hasNext()) { retain = false; Object next; if (which != Subject.PRIV_CREDENTIAL_SET) { next = e.next(); } else { next = (Object)java.security.AccessController.doPrivileged (new java.security.PrivilegedAction() { public Object run() { return e.next(); } }); } Iterator ce = c.iterator(); while (ce.hasNext()) { Object o = ce.next(); if (next == null) { if (o == null) { retain = true; break; } } else if (next.equals(o)) { retain = true; break; } } if (!retain) { e.remove(); retain = false; modified = true; } } return modified; } public void clear() { final Iterator e = iterator(); while (e.hasNext()) { Object next; if (which != Subject.PRIV_CREDENTIAL_SET) { next = e.next(); } else { next = (Object)java.security.AccessController.doPrivileged (new java.security.PrivilegedAction() { public Object run() { return e.next(); } }); } e.remove(); } } /** * Writes this object out to a stream (i.e., serializes it). * *

* * @serialData If this is a private credential set, * a security check is performed to ensure that * the caller has permission to access each credential * in the set. If the security check passes, * the set is serialized. */ private synchronized void writeObject(java.io.ObjectOutputStream oos) throws java.io.IOException { if (which == Subject.PRIV_CREDENTIAL_SET) { // check permissions before serializing Iterator i = iterator(); while (i.hasNext()) { i.next(); } } ObjectOutputStream.PutField fields = oos.putFields(); fields.put("this$0", subject); fields.put("elements", elements); fields.put("which", which); oos.writeFields(); } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ObjectInputStream.GetField fields = ois.readFields(); subject = (Subject) fields.get("this$0", null); elements = (LinkedList) fields.get("elements", null); which = fields.get("which", 0); } } /** * This class implements a Set which returns only * members that are an instance of a specified Class. */ private class ClassSet extends AbstractSet { private int which; private Class c; private Set set; ClassSet(int which, Class c) { this.which = which; this.c = c; set = new HashSet(); switch (which) { case Subject.PRINCIPAL_SET: synchronized(principals) { populateSet(); } break; case Subject.PUB_CREDENTIAL_SET: synchronized(pubCredentials) { populateSet(); } break; default: synchronized(privCredentials) { populateSet(); } break; } } private void populateSet() { final Iterator iterator; switch(which) { case Subject.PRINCIPAL_SET: iterator = Subject.this.principals.iterator(); break; case Subject.PUB_CREDENTIAL_SET: iterator = Subject.this.pubCredentials.iterator(); break; default: iterator = Subject.this.privCredentials.iterator(); break; } // Check whether the caller has permisson to get // credentials of Class c while (iterator.hasNext()) { Object next; if (which == Subject.PRIV_CREDENTIAL_SET) { next = (Object)java.security.AccessController.doPrivileged (new java.security.PrivilegedAction() { public Object run() { return iterator.next(); } }); } else { next = iterator.next(); } if (c.isAssignableFrom(next.getClass())) { if (which != Subject.PRIV_CREDENTIAL_SET) { set.add((T)next); } else { // Check permission for private creds SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new PrivateCredentialPermission (next.getClass().getName(), Subject.this.getPrincipals())); } set.add((T)next); } } } } public int size() { return set.size(); } public Iterator iterator() { return set.iterator(); } public boolean add(T o) { if (!o.getClass().isAssignableFrom(c)) { MessageFormat form = new MessageFormat(ResourcesMgr.getString ("attempting to add an object which is not an " + "instance of class")); Object[] source = {c.toString()}; throw new SecurityException(form.format(source)); } return set.add(o); } } }