| /* |
| * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Sun designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Sun in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * CA 95054 USA or visit www.sun.com if you need additional information or |
| * have any questions. |
| */ |
| |
| 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; |
| |
| /** |
| * <p> A <code>Subject</code> 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). |
| * |
| * <p> Subjects may potentially have multiple identities. |
| * Each identity is represented as a <code>Principal</code> |
| * within the <code>Subject</code>. Principals simply bind names to a |
| * <code>Subject</code>. For example, a <code>Subject</code> that happens |
| * to be a person, Alice, might have two Principals: |
| * one which binds "Alice Bar", the name on her driver license, |
| * to the <code>Subject</code>, and another which binds, |
| * "999-99-9999", the number on her student identification card, |
| * to the <code>Subject</code>. Both Principals refer to the same |
| * <code>Subject</code> even though each has a different name. |
| * |
| * <p> A <code>Subject</code> 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 |
| * <code>Set</code>. Credentials intended to be shared, such as |
| * public key certificates or Kerberos server tickets are stored |
| * within a public credential <code>Set</code>. Different permissions |
| * are required to access and modify the different credential Sets. |
| * |
| * <p> To retrieve all the Principals associated with a <code>Subject</code>, |
| * invoke the <code>getPrincipals</code> method. To retrieve |
| * all the public or private credentials belonging to a <code>Subject</code>, |
| * invoke the <code>getPublicCredentials</code> method or |
| * <code>getPrivateCredentials</code> method, respectively. |
| * To modify the returned <code>Set</code> of Principals and credentials, |
| * use the methods defined in the <code>Set</code> class. |
| * For example: |
| * <pre> |
| * Subject subject; |
| * Principal principal; |
| * Object credential; |
| * |
| * // add a Principal and credential to the Subject |
| * subject.getPrincipals().add(principal); |
| * subject.getPublicCredentials().add(credential); |
| * </pre> |
| * |
| * <p> This <code>Subject</code> class implements <code>Serializable</code>. |
| * While the Principals associated with the <code>Subject</code> are serialized, |
| * the credentials associated with the <code>Subject</code> are not. |
| * Note that the <code>java.security.Principal</code> class |
| * does not implement <code>Serializable</code>. Therefore all concrete |
| * <code>Principal</code> implementations associated with Subjects |
| * must implement <code>Serializable</code>. |
| * |
| * @see java.security.Principal |
| * @see java.security.DomainCombiner |
| */ |
| public final class Subject implements java.io.Serializable { |
| |
| private static final long serialVersionUID = -8308522755600156056L; |
| |
| /** |
| * A <code>Set</code> that provides a view of all of this |
| * Subject's Principals |
| * |
| * <p> |
| * |
| * @serial Each element in this set is a |
| * <code>java.security.Principal</code>. |
| * The set is a <code>Subject.SecureSet</code>. |
| */ |
| Set<Principal> principals; |
| |
| /** |
| * Sets that provide a view of all of this |
| * Subject's Credentials |
| */ |
| transient Set<Object> pubCredentials; |
| transient Set<Object> 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; |
| |
| private static final ProtectionDomain[] NULL_PD_ARRAY |
| = new ProtectionDomain[0]; |
| |
| /** |
| * Create an instance of a <code>Subject</code> |
| * with an empty <code>Set</code> of Principals and empty |
| * Sets of public and private credentials. |
| * |
| * <p> The newly constructed Sets check whether this <code>Subject</code> |
| * has been set read-only before permitting subsequent modifications. |
| * The newly created Sets also prevent illegal modifications |
| * by ensuring that callers have sufficient permissions. |
| * |
| * <p> To modify the Principals Set, the caller must have |
| * <code>AuthPermission("modifyPrincipals")</code>. |
| * To modify the public credential Set, the caller must have |
| * <code>AuthPermission("modifyPublicCredentials")</code>. |
| * To modify the private credential Set, the caller must have |
| * <code>AuthPermission("modifyPrivateCredentials")</code>. |
| */ |
| public Subject() { |
| |
| this.principals = Collections.synchronizedSet |
| (new SecureSet<Principal>(this, PRINCIPAL_SET)); |
| this.pubCredentials = Collections.synchronizedSet |
| (new SecureSet<Object>(this, PUB_CREDENTIAL_SET)); |
| this.privCredentials = Collections.synchronizedSet |
| (new SecureSet<Object>(this, PRIV_CREDENTIAL_SET)); |
| } |
| |
| /** |
| * Create an instance of a <code>Subject</code> with |
| * Principals and credentials. |
| * |
| * <p> The Principals and credentials from the specified Sets |
| * are copied into newly constructed Sets. |
| * These newly created Sets check whether this <code>Subject</code> |
| * has been set read-only before permitting subsequent modifications. |
| * The newly created Sets also prevent illegal modifications |
| * by ensuring that callers have sufficient permissions. |
| * |
| * <p> To modify the Principals Set, the caller must have |
| * <code>AuthPermission("modifyPrincipals")</code>. |
| * To modify the public credential Set, the caller must have |
| * <code>AuthPermission("modifyPublicCredentials")</code>. |
| * To modify the private credential Set, the caller must have |
| * <code>AuthPermission("modifyPrivateCredentials")</code>. |
| * <p> |
| * |
| * @param readOnly true if the <code>Subject</code> is to be read-only, |
| * and false otherwise. <p> |
| * |
| * @param principals the <code>Set</code> of Principals |
| * to be associated with this <code>Subject</code>. <p> |
| * |
| * @param pubCredentials the <code>Set</code> of public credentials |
| * to be associated with this <code>Subject</code>. <p> |
| * |
| * @param privCredentials the <code>Set</code> of private credentials |
| * to be associated with this <code>Subject</code>. |
| * |
| * @exception NullPointerException if the specified |
| * <code>principals</code>, <code>pubCredentials</code>, |
| * or <code>privCredentials</code> are <code>null</code>. |
| */ |
| public Subject(boolean readOnly, Set<? extends Principal> 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<Principal> |
| (this, PRINCIPAL_SET, principals)); |
| this.pubCredentials = Collections.synchronizedSet(new SecureSet<Object> |
| (this, PUB_CREDENTIAL_SET, pubCredentials)); |
| this.privCredentials = Collections.synchronizedSet(new SecureSet<Object> |
| (this, PRIV_CREDENTIAL_SET, privCredentials)); |
| this.readOnly = readOnly; |
| } |
| |
| /** |
| * Set this <code>Subject</code> to be read-only. |
| * |
| * <p> Modifications (additions and removals) to this Subject's |
| * <code>Principal</code> <code>Set</code> and |
| * credential Sets will be disallowed. |
| * The <code>destroy</code> operation on this Subject's credentials will |
| * still be permitted. |
| * |
| * <p> Subsequent attempts to modify the Subject's <code>Principal</code> |
| * and credential Sets will result in an |
| * <code>IllegalStateException</code> being thrown. |
| * Also, once a <code>Subject</code> is read-only, |
| * it can not be reset to being writable again. |
| * |
| * <p> |
| * |
| * @exception SecurityException if the caller does not have permission |
| * to set this <code>Subject</code> 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 <code>Subject</code> is read-only. |
| * |
| * <p> |
| * |
| * @return true if this <code>Subject</code> is read-only, false otherwise. |
| */ |
| public boolean isReadOnly() { |
| return this.readOnly; |
| } |
| |
| /** |
| * Get the <code>Subject</code> associated with the provided |
| * <code>AccessControlContext</code>. |
| * |
| * <p> The <code>AccessControlContext</code> may contain many |
| * Subjects (from nested <code>doAs</code> calls). |
| * In this situation, the most recent <code>Subject</code> associated |
| * with the <code>AccessControlContext</code> is returned. |
| * |
| * <p> |
| * |
| * @param acc the <code>AccessControlContext</code> from which to retrieve |
| * the <code>Subject</code>. |
| * |
| * @return the <code>Subject</code> associated with the provided |
| * <code>AccessControlContext</code>, or <code>null</code> |
| * if no <code>Subject</code> is associated |
| * with the provided <code>AccessControlContext</code>. |
| * |
| * @exception SecurityException if the caller does not have permission |
| * to get the <code>Subject</code>. <p> |
| * |
| * @exception NullPointerException if the provided |
| * <code>AccessControlContext</code> is <code>null</code>. |
| */ |
| 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 AccessController.doPrivileged |
| (new java.security.PrivilegedAction<Subject>() { |
| public Subject run() { |
| DomainCombiner dc = acc.getDomainCombiner(); |
| if (!(dc instanceof SubjectDomainCombiner)) |
| return null; |
| SubjectDomainCombiner sdc = (SubjectDomainCombiner)dc; |
| return sdc.getSubject(); |
| } |
| }); |
| } |
| |
| /** |
| * Perform work as a particular <code>Subject</code>. |
| * |
| * <p> This method first retrieves the current Thread's |
| * <code>AccessControlContext</code> via |
| * <code>AccessController.getContext</code>, |
| * and then instantiates a new <code>AccessControlContext</code> |
| * using the retrieved context along with a new |
| * <code>SubjectDomainCombiner</code> (constructed using |
| * the provided <code>Subject</code>). |
| * Finally, this method invokes <code>AccessController.doPrivileged</code>, |
| * passing it the provided <code>PrivilegedAction</code>, |
| * as well as the newly constructed <code>AccessControlContext</code>. |
| * |
| * <p> |
| * |
| * @param subject the <code>Subject</code> that the specified |
| * <code>action</code> will run as. This parameter |
| * may be <code>null</code>. <p> |
| * |
| * @param action the code to be run as the specified |
| * <code>Subject</code>. <p> |
| * |
| * @return the value returned by the PrivilegedAction's |
| * <code>run</code> method. |
| * |
| * @exception NullPointerException if the <code>PrivilegedAction</code> |
| * is <code>null</code>. <p> |
| * |
| * @exception SecurityException if the caller does not have permission |
| * to invoke this method. |
| */ |
| public static <T> T doAs(final Subject subject, |
| final java.security.PrivilegedAction<T> 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 <code>Subject</code>. |
| * |
| * <p> This method first retrieves the current Thread's |
| * <code>AccessControlContext</code> via |
| * <code>AccessController.getContext</code>, |
| * and then instantiates a new <code>AccessControlContext</code> |
| * using the retrieved context along with a new |
| * <code>SubjectDomainCombiner</code> (constructed using |
| * the provided <code>Subject</code>). |
| * Finally, this method invokes <code>AccessController.doPrivileged</code>, |
| * passing it the provided <code>PrivilegedExceptionAction</code>, |
| * as well as the newly constructed <code>AccessControlContext</code>. |
| * |
| * <p> |
| * |
| * @param subject the <code>Subject</code> that the specified |
| * <code>action</code> will run as. This parameter |
| * may be <code>null</code>. <p> |
| * |
| * @param action the code to be run as the specified |
| * <code>Subject</code>. <p> |
| * |
| * @return the value returned by the |
| * PrivilegedExceptionAction's <code>run</code> method. |
| * |
| * @exception PrivilegedActionException if the |
| * <code>PrivilegedExceptionAction.run</code> |
| * method throws a checked exception. <p> |
| * |
| * @exception NullPointerException if the specified |
| * <code>PrivilegedExceptionAction</code> is |
| * <code>null</code>. <p> |
| * |
| * @exception SecurityException if the caller does not have permission |
| * to invoke this method. |
| */ |
| public static <T> T doAs(final Subject subject, |
| final java.security.PrivilegedExceptionAction<T> 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 <code>Subject</code>. |
| * |
| * <p> This method behaves exactly as <code>Subject.doAs</code>, |
| * except that instead of retrieving the current Thread's |
| * <code>AccessControlContext</code>, it uses the provided |
| * <code>AccessControlContext</code>. If the provided |
| * <code>AccessControlContext</code> is <code>null</code>, |
| * this method instantiates a new <code>AccessControlContext</code> |
| * with an empty collection of ProtectionDomains. |
| * |
| * <p> |
| * |
| * @param subject the <code>Subject</code> that the specified |
| * <code>action</code> will run as. This parameter |
| * may be <code>null</code>. <p> |
| * |
| * @param action the code to be run as the specified |
| * <code>Subject</code>. <p> |
| * |
| * @param acc the <code>AccessControlContext</code> to be tied to the |
| * specified <i>subject</i> and <i>action</i>. <p> |
| * |
| * @return the value returned by the PrivilegedAction's |
| * <code>run</code> method. |
| * |
| * @exception NullPointerException if the <code>PrivilegedAction</code> |
| * is <code>null</code>. <p> |
| * |
| * @exception SecurityException if the caller does not have permission |
| * to invoke this method. |
| */ |
| public static <T> T doAsPrivileged(final Subject subject, |
| final java.security.PrivilegedAction<T> 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(NULL_PD_ARRAY) : |
| 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 <code>Subject</code>. |
| * |
| * <p> This method behaves exactly as <code>Subject.doAs</code>, |
| * except that instead of retrieving the current Thread's |
| * <code>AccessControlContext</code>, it uses the provided |
| * <code>AccessControlContext</code>. If the provided |
| * <code>AccessControlContext</code> is <code>null</code>, |
| * this method instantiates a new <code>AccessControlContext</code> |
| * with an empty collection of ProtectionDomains. |
| * |
| * <p> |
| * |
| * @param subject the <code>Subject</code> that the specified |
| * <code>action</code> will run as. This parameter |
| * may be <code>null</code>. <p> |
| * |
| * @param action the code to be run as the specified |
| * <code>Subject</code>. <p> |
| * |
| * @param acc the <code>AccessControlContext</code> to be tied to the |
| * specified <i>subject</i> and <i>action</i>. <p> |
| * |
| * @return the value returned by the |
| * PrivilegedExceptionAction's <code>run</code> method. |
| * |
| * @exception PrivilegedActionException if the |
| * <code>PrivilegedExceptionAction.run</code> |
| * method throws a checked exception. <p> |
| * |
| * @exception NullPointerException if the specified |
| * <code>PrivilegedExceptionAction</code> is |
| * <code>null</code>. <p> |
| * |
| * @exception SecurityException if the caller does not have permission |
| * to invoke this method. |
| */ |
| public static <T> T doAsPrivileged(final Subject subject, |
| final java.security.PrivilegedExceptionAction<T> 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(NULL_PD_ARRAY) : |
| 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 java.security.AccessController.doPrivileged |
| (new java.security.PrivilegedAction<AccessControlContext>() { |
| public AccessControlContext run() { |
| if (subject == null) |
| return new AccessControlContext(acc, null); |
| else |
| return new AccessControlContext |
| (acc, |
| new SubjectDomainCombiner(subject)); |
| } |
| }); |
| } |
| |
| /** |
| * Return the <code>Set</code> of Principals associated with this |
| * <code>Subject</code>. Each <code>Principal</code> represents |
| * an identity for this <code>Subject</code>. |
| * |
| * <p> The returned <code>Set</code> is backed by this Subject's |
| * internal <code>Principal</code> <code>Set</code>. Any modification |
| * to the returned <code>Set</code> affects the internal |
| * <code>Principal</code> <code>Set</code> as well. |
| * |
| * <p> |
| * |
| * @return The <code>Set</code> of Principals associated with this |
| * <code>Subject</code>. |
| */ |
| public Set<Principal> getPrincipals() { |
| |
| // always return an empty Set instead of null |
| // so LoginModules can add to the Set if necessary |
| return principals; |
| } |
| |
| /** |
| * Return a <code>Set</code> of Principals associated with this |
| * <code>Subject</code> that are instances or subclasses of the specified |
| * <code>Class</code>. |
| * |
| * <p> The returned <code>Set</code> is not backed by this Subject's |
| * internal <code>Principal</code> <code>Set</code>. A new |
| * <code>Set</code> is created and returned for each method invocation. |
| * Modifications to the returned <code>Set</code> |
| * will not affect the internal <code>Principal</code> <code>Set</code>. |
| * |
| * <p> |
| * |
| * @param c the returned <code>Set</code> of Principals will all be |
| * instances of this class. |
| * |
| * @return a <code>Set</code> of Principals that are instances of the |
| * specified <code>Class</code>. |
| * |
| * @exception NullPointerException if the specified <code>Class</code> |
| * is <code>null</code>. |
| */ |
| public <T extends Principal> Set<T> getPrincipals(Class<T> 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<T>(PRINCIPAL_SET, c); |
| } |
| |
| /** |
| * Return the <code>Set</code> of public credentials held by this |
| * <code>Subject</code>. |
| * |
| * <p> The returned <code>Set</code> is backed by this Subject's |
| * internal public Credential <code>Set</code>. Any modification |
| * to the returned <code>Set</code> affects the internal public |
| * Credential <code>Set</code> as well. |
| * |
| * <p> |
| * |
| * @return A <code>Set</code> of public credentials held by this |
| * <code>Subject</code>. |
| */ |
| public Set<Object> getPublicCredentials() { |
| |
| // always return an empty Set instead of null |
| // so LoginModules can add to the Set if necessary |
| return pubCredentials; |
| } |
| |
| /** |
| * Return the <code>Set</code> of private credentials held by this |
| * <code>Subject</code>. |
| * |
| * <p> The returned <code>Set</code> is backed by this Subject's |
| * internal private Credential <code>Set</code>. Any modification |
| * to the returned <code>Set</code> affects the internal private |
| * Credential <code>Set</code> as well. |
| * |
| * <p> A caller requires permissions to access the Credentials |
| * in the returned <code>Set</code>, or to modify the |
| * <code>Set</code> itself. A <code>SecurityException</code> |
| * is thrown if the caller does not have the proper permissions. |
| * |
| * <p> While iterating through the <code>Set</code>, |
| * a <code>SecurityException</code> is thrown |
| * if the caller does not have permission to access a |
| * particular Credential. The <code>Iterator</code> |
| * is nevertheless advanced to next element in the <code>Set</code>. |
| * |
| * <p> |
| * |
| * @return A <code>Set</code> of private credentials held by this |
| * <code>Subject</code>. |
| */ |
| public Set<Object> 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 <code>Set</code> of public credentials associated with this |
| * <code>Subject</code> that are instances or subclasses of the specified |
| * <code>Class</code>. |
| * |
| * <p> The returned <code>Set</code> is not backed by this Subject's |
| * internal public Credential <code>Set</code>. A new |
| * <code>Set</code> is created and returned for each method invocation. |
| * Modifications to the returned <code>Set</code> |
| * will not affect the internal public Credential <code>Set</code>. |
| * |
| * <p> |
| * |
| * @param c the returned <code>Set</code> of public credentials will all be |
| * instances of this class. |
| * |
| * @return a <code>Set</code> of public credentials that are instances |
| * of the specified <code>Class</code>. |
| * |
| * @exception NullPointerException if the specified <code>Class</code> |
| * is <code>null</code>. |
| */ |
| public <T> Set<T> getPublicCredentials(Class<T> 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<T>(PUB_CREDENTIAL_SET, c); |
| } |
| |
| /** |
| * Return a <code>Set</code> of private credentials associated with this |
| * <code>Subject</code> that are instances or subclasses of the specified |
| * <code>Class</code>. |
| * |
| * <p> The caller must have permission to access all of the |
| * requested Credentials, or a <code>SecurityException</code> |
| * will be thrown. |
| * |
| * <p> The returned <code>Set</code> is not backed by this Subject's |
| * internal private Credential <code>Set</code>. A new |
| * <code>Set</code> is created and returned for each method invocation. |
| * Modifications to the returned <code>Set</code> |
| * will not affect the internal private Credential <code>Set</code>. |
| * |
| * <p> |
| * |
| * @param c the returned <code>Set</code> of private credentials will all be |
| * instances of this class. |
| * |
| * @return a <code>Set</code> of private credentials that are instances |
| * of the specified <code>Class</code>. |
| * |
| * @exception NullPointerException if the specified <code>Class</code> |
| * is <code>null</code>. |
| */ |
| public <T> Set<T> getPrivateCredentials(Class<T> 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<T>(PRIV_CREDENTIAL_SET, c); |
| } |
| |
| /** |
| * Compares the specified Object with this <code>Subject</code> |
| * for equality. Returns true if the given object is also a Subject |
| * and the two <code>Subject</code> instances are equivalent. |
| * More formally, two <code>Subject</code> instances are |
| * equal if their <code>Principal</code> and <code>Credential</code> |
| * Sets are equal. |
| * |
| * <p> |
| * |
| * @param o Object to be compared for equality with this |
| * <code>Subject</code>. |
| * |
| * @return true if the specified Object is equal to this |
| * <code>Subject</code>. |
| * |
| * @exception SecurityException if the caller does not have permission |
| * to access the private credentials for this <code>Subject</code>, |
| * or if the caller does not have permission to access the |
| * private credentials for the provided <code>Subject</code>. |
| */ |
| 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<Principal> thatPrincipals; |
| synchronized(that.principals) { |
| // avoid deadlock from dual locks |
| thatPrincipals = new HashSet<Principal>(that.principals); |
| } |
| if (!principals.equals(thatPrincipals)) { |
| return false; |
| } |
| |
| Set<Object> thatPubCredentials; |
| synchronized(that.pubCredentials) { |
| // avoid deadlock from dual locks |
| thatPubCredentials = new HashSet<Object>(that.pubCredentials); |
| } |
| if (!pubCredentials.equals(thatPubCredentials)) { |
| return false; |
| } |
| |
| Set<Object> thatPrivCredentials; |
| synchronized(that.privCredentials) { |
| // avoid deadlock from dual locks |
| thatPrivCredentials = new HashSet<Object>(that.privCredentials); |
| } |
| if (!privCredentials.equals(thatPrivCredentials)) { |
| return false; |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Return the String representation of this <code>Subject</code>. |
| * |
| * <p> |
| * |
| * @return the String representation of this <code>Subject</code>. |
| */ |
| 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 = ResourcesMgr.getString("Subject:\n"); |
| String suffix = ""; |
| |
| synchronized(principals) { |
| Iterator<Principal> pI = principals.iterator(); |
| while (pI.hasNext()) { |
| Principal p = pI.next(); |
| suffix = suffix + ResourcesMgr.getString("\tPrincipal: ") + |
| p.toString() + ResourcesMgr.getString("\n"); |
| } |
| } |
| |
| synchronized(pubCredentials) { |
| Iterator<Object> 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<Object> 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 <code>Subject</code>. |
| * |
| * <p> |
| * |
| * @return a hashcode for this <code>Subject</code>. |
| * |
| * @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 |
| * (<code>credential.hashCode()</code> throws an |
| * <code>IllegalStateException</code>), |
| * the hashcode for that credential is derived via: |
| * <code>credential.getClass().toString().hashCode()</code>. |
| */ |
| |
| int hashCode = 0; |
| |
| synchronized(principals) { |
| Iterator<Principal> pIterator = principals.iterator(); |
| while (pIterator.hasNext()) { |
| Principal p = pIterator.next(); |
| hashCode ^= p.hashCode(); |
| } |
| } |
| |
| synchronized(pubCredentials) { |
| Iterator<Object> 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 <code>Set</code> is not serialized, but we do not |
| // want the default deserialization routine to set it to null. |
| this.pubCredentials = Collections.synchronizedSet |
| (new SecureSet<Object>(this, PUB_CREDENTIAL_SET)); |
| this.privCredentials = Collections.synchronizedSet |
| (new SecureSet<Object>(this, PRIV_CREDENTIAL_SET)); |
| } |
| |
| /** |
| * Prevent modifications unless caller has permission. |
| * |
| * @serial include |
| */ |
| private static class SecureSet<E> |
| extends AbstractSet<E> |
| 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<E> elements; |
| |
| /** |
| * @serial An integer identifying the type of objects contained |
| * in this set. If <code>which == 1</code>, |
| * this is a Principal set and all the elements are |
| * of type <code>java.security.Principal</code>. |
| * If <code>which == 2</code>, this is a public credential |
| * set and all the elements are of type <code>Object</code>. |
| * If <code>which == 3</code>, this is a private credential |
| * set and all the elements are of type <code>Object</code>. |
| */ |
| private int which; |
| |
| SecureSet(Subject subject, int which) { |
| this.subject = subject; |
| this.which = which; |
| this.elements = new LinkedList<E>(); |
| } |
| |
| SecureSet(Subject subject, int which, Set<? extends E> set) { |
| this.subject = subject; |
| this.which = which; |
| this.elements = new LinkedList<E>(set); |
| } |
| |
| public int size() { |
| return elements.size(); |
| } |
| |
| public Iterator<E> iterator() { |
| final LinkedList<E> list = elements; |
| return new Iterator<E>() { |
| ListIterator<E> i = list.listIterator(0); |
| |
| public boolean hasNext() {return i.hasNext();} |
| |
| public E 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(E 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> e = iterator(); |
| while (e.hasNext()) { |
| E next; |
| if (which != Subject.PRIV_CREDENTIAL_SET) { |
| next = e.next(); |
| } else { |
| next = java.security.AccessController.doPrivileged |
| (new java.security.PrivilegedAction<E>() { |
| public E 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> e = iterator(); |
| while (e.hasNext()) { |
| E 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 = java.security.AccessController.doPrivileged |
| (new java.security.PrivilegedAction<E>() { |
| public E 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> e = iterator(); |
| while (e.hasNext()) { |
| E next; |
| if (which != Subject.PRIV_CREDENTIAL_SET) { |
| next = e.next(); |
| } else { |
| next = java.security.AccessController.doPrivileged |
| (new java.security.PrivilegedAction<E>() { |
| public E 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> e = iterator(); |
| while (e.hasNext()) { |
| retain = false; |
| E next; |
| if (which != Subject.PRIV_CREDENTIAL_SET) { |
| next = e.next(); |
| } else { |
| next = java.security.AccessController.doPrivileged |
| (new java.security.PrivilegedAction<E>() { |
| public E 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> e = iterator(); |
| while (e.hasNext()) { |
| E next; |
| if (which != Subject.PRIV_CREDENTIAL_SET) { |
| next = e.next(); |
| } else { |
| next = java.security.AccessController.doPrivileged |
| (new java.security.PrivilegedAction<E>() { |
| public E run() { |
| return e.next(); |
| } |
| }); |
| } |
| e.remove(); |
| } |
| } |
| |
| /** |
| * Writes this object out to a stream (i.e., serializes it). |
| * |
| * <p> |
| * |
| * @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 void writeObject(java.io.ObjectOutputStream oos) |
| throws java.io.IOException { |
| |
| if (which == Subject.PRIV_CREDENTIAL_SET) { |
| // check permissions before serializing |
| Iterator<E> 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<E>) fields.get("elements", null); |
| which = fields.get("which", 0); |
| } |
| } |
| |
| /** |
| * This class implements a <code>Set</code> which returns only |
| * members that are an instance of a specified Class. |
| */ |
| private class ClassSet<T> extends AbstractSet<T> { |
| |
| private int which; |
| private Class<T> c; |
| private Set<T> set; |
| |
| ClassSet(int which, Class<T> c) { |
| this.which = which; |
| this.c = c; |
| set = new HashSet<T>(); |
| |
| 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 = java.security.AccessController.doPrivileged |
| (new java.security.PrivilegedAction<Object>() { |
| 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<T> 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); |
| } |
| } |
| } |