Path: blob/master/src/java.base/share/classes/javax/security/auth/Subject.java
41159 views
/*1* Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.security.auth;2627import java.util.*;28import java.io.*;29import java.lang.reflect.*;30import java.text.MessageFormat;31import java.security.AccessController;32import java.security.AccessControlContext;33import java.security.DomainCombiner;34import java.security.Permission;35import java.security.PermissionCollection;36import java.security.Principal;37import java.security.PrivilegedAction;38import java.security.PrivilegedExceptionAction;39import java.security.PrivilegedActionException;40import java.security.ProtectionDomain;41import sun.security.util.ResourcesMgr;4243/**44* <p> A {@code Subject} represents a grouping of related information45* for a single entity, such as a person.46* Such information includes the Subject's identities as well as47* its security-related attributes48* (passwords and cryptographic keys, for example).49*50* <p> Subjects may potentially have multiple identities.51* Each identity is represented as a {@code Principal}52* within the {@code Subject}. Principals simply bind names to a53* {@code Subject}. For example, a {@code Subject} that happens54* to be a person, Alice, might have two Principals:55* one which binds "Alice Bar", the name on her driver license,56* to the {@code Subject}, and another which binds,57* "999-99-9999", the number on her student identification card,58* to the {@code Subject}. Both Principals refer to the same59* {@code Subject} even though each has a different name.60*61* <p> A {@code Subject} may also own security-related attributes,62* which are referred to as credentials.63* Sensitive credentials that require special protection, such as64* private cryptographic keys, are stored within a private credential65* {@code Set}. Credentials intended to be shared, such as66* public key certificates or Kerberos server tickets are stored67* within a public credential {@code Set}. Different permissions68* are required to access and modify the different credential Sets.69*70* <p> To retrieve all the Principals associated with a {@code Subject},71* invoke the {@code getPrincipals} method. To retrieve72* all the public or private credentials belonging to a {@code Subject},73* invoke the {@code getPublicCredentials} method or74* {@code getPrivateCredentials} method, respectively.75* To modify the returned {@code Set} of Principals and credentials,76* use the methods defined in the {@code Set} class.77* For example:78* <pre>79* Subject subject;80* Principal principal;81* Object credential;82*83* // add a Principal and credential to the Subject84* subject.getPrincipals().add(principal);85* subject.getPublicCredentials().add(credential);86* </pre>87*88* <p> This {@code Subject} class implements {@code Serializable}.89* While the Principals associated with the {@code Subject} are serialized,90* the credentials associated with the {@code Subject} are not.91* Note that the {@code java.security.Principal} class92* does not implement {@code Serializable}. Therefore all concrete93* {@code Principal} implementations associated with Subjects94* must implement {@code Serializable}.95*96* @since 1.497* @see java.security.Principal98* @see java.security.DomainCombiner99*/100public final class Subject implements java.io.Serializable {101102@java.io.Serial103private static final long serialVersionUID = -8308522755600156056L;104105/**106* A {@code Set} that provides a view of all of this107* Subject's Principals108*109* @serial Each element in this set is a110* {@code java.security.Principal}.111* The set is a {@code Subject.SecureSet}.112*/113@SuppressWarnings("serial") // Not statically typed as Serializable114Set<Principal> principals;115116/**117* Sets that provide a view of all of this118* Subject's Credentials119*/120transient Set<Object> pubCredentials;121transient Set<Object> privCredentials;122123/**124* Whether this Subject is read-only125*126* @serial127*/128private volatile boolean readOnly;129130private static final int PRINCIPAL_SET = 1;131private static final int PUB_CREDENTIAL_SET = 2;132private static final int PRIV_CREDENTIAL_SET = 3;133134private static final ProtectionDomain[] NULL_PD_ARRAY135= new ProtectionDomain[0];136137/**138* Create an instance of a {@code Subject}139* with an empty {@code Set} of Principals and empty140* Sets of public and private credentials.141*142* <p> The newly constructed Sets check whether this {@code Subject}143* has been set read-only before permitting subsequent modifications.144* The newly created Sets also prevent illegal modifications145* by ensuring that callers have sufficient permissions. These Sets146* also prohibit null elements, and attempts to add or query a null147* element will result in a {@code NullPointerException}.148*149* <p> To modify the Principals Set, the caller must have150* {@code AuthPermission("modifyPrincipals")}.151* To modify the public credential Set, the caller must have152* {@code AuthPermission("modifyPublicCredentials")}.153* To modify the private credential Set, the caller must have154* {@code AuthPermission("modifyPrivateCredentials")}.155*/156public Subject() {157158this.principals = Collections.synchronizedSet159(new SecureSet<>(this, PRINCIPAL_SET));160this.pubCredentials = Collections.synchronizedSet161(new SecureSet<>(this, PUB_CREDENTIAL_SET));162this.privCredentials = Collections.synchronizedSet163(new SecureSet<>(this, PRIV_CREDENTIAL_SET));164}165166/**167* Create an instance of a {@code Subject} with168* Principals and credentials.169*170* <p> The Principals and credentials from the specified Sets171* are copied into newly constructed Sets.172* These newly created Sets check whether this {@code Subject}173* has been set read-only before permitting subsequent modifications.174* The newly created Sets also prevent illegal modifications175* by ensuring that callers have sufficient permissions. These Sets176* also prohibit null elements, and attempts to add or query a null177* element will result in a {@code NullPointerException}.178*179* <p> To modify the Principals Set, the caller must have180* {@code AuthPermission("modifyPrincipals")}.181* To modify the public credential Set, the caller must have182* {@code AuthPermission("modifyPublicCredentials")}.183* To modify the private credential Set, the caller must have184* {@code AuthPermission("modifyPrivateCredentials")}.185*186* @param readOnly true if the {@code Subject} is to be read-only,187* and false otherwise.188*189* @param principals the {@code Set} of Principals190* to be associated with this {@code Subject}.191*192* @param pubCredentials the {@code Set} of public credentials193* to be associated with this {@code Subject}.194*195* @param privCredentials the {@code Set} of private credentials196* to be associated with this {@code Subject}.197*198* @throws NullPointerException if the specified199* {@code principals}, {@code pubCredentials},200* or {@code privCredentials} are {@code null},201* or a null value exists within any of these three202* Sets.203*/204public Subject(boolean readOnly, Set<? extends Principal> principals,205Set<?> pubCredentials, Set<?> privCredentials) {206LinkedList<Principal> principalList207= collectionNullClean(principals);208LinkedList<Object> pubCredsList209= collectionNullClean(pubCredentials);210LinkedList<Object> privCredsList211= collectionNullClean(privCredentials);212213this.principals = Collections.synchronizedSet(214new SecureSet<>(this, PRINCIPAL_SET, principalList));215this.pubCredentials = Collections.synchronizedSet(216new SecureSet<>(this, PUB_CREDENTIAL_SET, pubCredsList));217this.privCredentials = Collections.synchronizedSet(218new SecureSet<>(this, PRIV_CREDENTIAL_SET, privCredsList));219this.readOnly = readOnly;220}221222/**223* Set this {@code Subject} to be read-only.224*225* <p> Modifications (additions and removals) to this Subject's226* {@code Principal} {@code Set} and227* credential Sets will be disallowed.228* The {@code destroy} operation on this Subject's credentials will229* still be permitted.230*231* <p> Subsequent attempts to modify the Subject's {@code Principal}232* and credential Sets will result in an233* {@code IllegalStateException} being thrown.234* Also, once a {@code Subject} is read-only,235* it can not be reset to being writable again.236*237* @throws SecurityException if a security manager is installed and the238* caller does not have an239* {@link AuthPermission#AuthPermission(String)240* AuthPermission("setReadOnly")} permission to set this241* {@code Subject} to be read-only.242*/243public void setReadOnly() {244@SuppressWarnings("removal")245java.lang.SecurityManager sm = System.getSecurityManager();246if (sm != null) {247sm.checkPermission(AuthPermissionHolder.SET_READ_ONLY_PERMISSION);248}249250this.readOnly = true;251}252253/**254* Query whether this {@code Subject} is read-only.255*256* @return true if this {@code Subject} is read-only, false otherwise.257*/258public boolean isReadOnly() {259return this.readOnly;260}261262/**263* Get the {@code Subject} associated with the provided264* {@code AccessControlContext}.265*266* <p> The {@code AccessControlContext} may contain many267* Subjects (from nested {@code doAs} calls).268* In this situation, the most recent {@code Subject} associated269* with the {@code AccessControlContext} is returned.270*271* @param acc the {@code AccessControlContext} from which to retrieve272* the {@code Subject}.273*274* @return the {@code Subject} associated with the provided275* {@code AccessControlContext}, or {@code null}276* if no {@code Subject} is associated277* with the provided {@code AccessControlContext}.278*279* @throws SecurityException if a security manager is installed and the280* caller does not have an281* {@link AuthPermission#AuthPermission(String)282* AuthPermission("getSubject")} permission to get the283* {@code Subject}.284*285* @throws NullPointerException if the provided286* {@code AccessControlContext} is {@code null}.287*288* @deprecated This method depends on {@link AccessControlContext}289* which, in conjunction with290* {@linkplain SecurityManager the Security Manager}, is deprecated291* and subject to removal in a future release. However, obtaining a292* Subject is useful independent of the Security Manager, so a293* replacement for this method may be added in a future release.294*/295@SuppressWarnings("removal")296@Deprecated(since="17", forRemoval=true)297public static Subject getSubject(final AccessControlContext acc) {298299java.lang.SecurityManager sm = System.getSecurityManager();300if (sm != null) {301sm.checkPermission(AuthPermissionHolder.GET_SUBJECT_PERMISSION);302}303304Objects.requireNonNull(acc, ResourcesMgr.getString305("invalid.null.AccessControlContext.provided"));306307// return the Subject from the DomainCombiner of the provided context308return AccessController.doPrivileged309(new java.security.PrivilegedAction<>() {310public Subject run() {311DomainCombiner dc = acc.getDomainCombiner();312if (!(dc instanceof SubjectDomainCombiner)) {313return null;314}315SubjectDomainCombiner sdc = (SubjectDomainCombiner)dc;316return sdc.getSubject();317}318});319}320321/**322* Perform work as a particular {@code Subject}.323*324* <p> This method first retrieves the current Thread's325* {@code AccessControlContext} via326* {@code AccessController.getContext},327* and then instantiates a new {@code AccessControlContext}328* using the retrieved context along with a new329* {@code SubjectDomainCombiner} (constructed using330* the provided {@code Subject}).331* Finally, this method invokes {@code AccessController.doPrivileged},332* passing it the provided {@code PrivilegedAction},333* as well as the newly constructed {@code AccessControlContext}.334*335* @param subject the {@code Subject} that the specified336* {@code action} will run as. This parameter337* may be {@code null}.338*339* @param <T> the type of the value returned by the PrivilegedAction's340* {@code run} method.341*342* @param action the code to be run as the specified343* {@code Subject}.344*345* @return the value returned by the PrivilegedAction's346* {@code run} method.347*348* @throws NullPointerException if the {@code PrivilegedAction}349* is {@code null}.350*351* @throws SecurityException if a security manager is installed and the352* caller does not have an353* {@link AuthPermission#AuthPermission(String)354* AuthPermission("doAs")} permission to invoke this355* method.356*/357@SuppressWarnings("removal")358public static <T> T doAs(final Subject subject,359final java.security.PrivilegedAction<T> action) {360361java.lang.SecurityManager sm = System.getSecurityManager();362if (sm != null) {363sm.checkPermission(AuthPermissionHolder.DO_AS_PERMISSION);364}365366Objects.requireNonNull(action,367ResourcesMgr.getString("invalid.null.action.provided"));368369// set up the new Subject-based AccessControlContext370// for doPrivileged371final AccessControlContext currentAcc = AccessController.getContext();372373// call doPrivileged and push this new context on the stack374return java.security.AccessController.doPrivileged375(action,376createContext(subject, currentAcc));377}378379/**380* Perform work as a particular {@code Subject}.381*382* <p> This method first retrieves the current Thread's383* {@code AccessControlContext} via384* {@code AccessController.getContext},385* and then instantiates a new {@code AccessControlContext}386* using the retrieved context along with a new387* {@code SubjectDomainCombiner} (constructed using388* the provided {@code Subject}).389* Finally, this method invokes {@code AccessController.doPrivileged},390* passing it the provided {@code PrivilegedExceptionAction},391* as well as the newly constructed {@code AccessControlContext}.392*393* @param subject the {@code Subject} that the specified394* {@code action} will run as. This parameter395* may be {@code null}.396*397* @param <T> the type of the value returned by the398* PrivilegedExceptionAction's {@code run} method.399*400* @param action the code to be run as the specified401* {@code Subject}.402*403* @return the value returned by the404* PrivilegedExceptionAction's {@code run} method.405*406* @throws PrivilegedActionException if the407* {@code PrivilegedExceptionAction.run}408* method throws a checked exception.409*410* @throws NullPointerException if the specified411* {@code PrivilegedExceptionAction} is412* {@code null}.413*414* @throws SecurityException if a security manager is installed and the415* caller does not have an416* {@link AuthPermission#AuthPermission(String)417* AuthPermission("doAs")} permission to invoke this418* method.419*/420@SuppressWarnings("removal")421public static <T> T doAs(final Subject subject,422final java.security.PrivilegedExceptionAction<T> action)423throws java.security.PrivilegedActionException {424425java.lang.SecurityManager sm = System.getSecurityManager();426if (sm != null) {427sm.checkPermission(AuthPermissionHolder.DO_AS_PERMISSION);428}429430Objects.requireNonNull(action,431ResourcesMgr.getString("invalid.null.action.provided"));432433// set up the new Subject-based AccessControlContext for doPrivileged434final AccessControlContext currentAcc = AccessController.getContext();435436// call doPrivileged and push this new context on the stack437return java.security.AccessController.doPrivileged438(action,439createContext(subject, currentAcc));440}441442/**443* Perform privileged work as a particular {@code Subject}.444*445* <p> This method behaves exactly as {@code Subject.doAs},446* except that instead of retrieving the current Thread's447* {@code AccessControlContext}, it uses the provided448* {@code AccessControlContext}. If the provided449* {@code AccessControlContext} is {@code null},450* this method instantiates a new {@code AccessControlContext}451* with an empty collection of ProtectionDomains.452*453* @param subject the {@code Subject} that the specified454* {@code action} will run as. This parameter455* may be {@code null}.456*457* @param <T> the type of the value returned by the PrivilegedAction's458* {@code run} method.459*460* @param action the code to be run as the specified461* {@code Subject}.462*463* @param acc the {@code AccessControlContext} to be tied to the464* specified <i>subject</i> and <i>action</i>.465*466* @return the value returned by the PrivilegedAction's467* {@code run} method.468*469* @throws NullPointerException if the {@code PrivilegedAction}470* is {@code null}.471*472* @throws SecurityException if a security manager is installed and the473* caller does not have a474* {@link AuthPermission#AuthPermission(String)475* AuthPermission("doAsPrivileged")} permission to invoke476* this method.477*478* @deprecated This method is only useful in conjunction with479* {@linkplain SecurityManager the Security Manager}, which is480* deprecated and subject to removal in a future release.481* Consequently, this method is also deprecated and subject to482* removal. There is no replacement for the Security Manager or this483* method.484*/485@SuppressWarnings("removal")486@Deprecated(since="17", forRemoval=true)487public static <T> T doAsPrivileged(final Subject subject,488final java.security.PrivilegedAction<T> action,489final java.security.AccessControlContext acc) {490491java.lang.SecurityManager sm = System.getSecurityManager();492if (sm != null) {493sm.checkPermission(AuthPermissionHolder.DO_AS_PRIVILEGED_PERMISSION);494}495496Objects.requireNonNull(action,497ResourcesMgr.getString("invalid.null.action.provided"));498499// set up the new Subject-based AccessControlContext500// for doPrivileged501final AccessControlContext callerAcc =502(acc == null ?503new AccessControlContext(NULL_PD_ARRAY) :504acc);505506// call doPrivileged and push this new context on the stack507return java.security.AccessController.doPrivileged508(action,509createContext(subject, callerAcc));510}511512/**513* Perform privileged work as a particular {@code Subject}.514*515* <p> This method behaves exactly as {@code Subject.doAs},516* except that instead of retrieving the current Thread's517* {@code AccessControlContext}, it uses the provided518* {@code AccessControlContext}. If the provided519* {@code AccessControlContext} is {@code null},520* this method instantiates a new {@code AccessControlContext}521* with an empty collection of ProtectionDomains.522*523* @param subject the {@code Subject} that the specified524* {@code action} will run as. This parameter525* may be {@code null}.526*527* @param <T> the type of the value returned by the528* PrivilegedExceptionAction's {@code run} method.529*530* @param action the code to be run as the specified531* {@code Subject}.532*533* @param acc the {@code AccessControlContext} to be tied to the534* specified <i>subject</i> and <i>action</i>.535*536* @return the value returned by the537* PrivilegedExceptionAction's {@code run} method.538*539* @throws PrivilegedActionException if the540* {@code PrivilegedExceptionAction.run}541* method throws a checked exception.542*543* @throws NullPointerException if the specified544* {@code PrivilegedExceptionAction} is545* {@code null}.546*547* @throws SecurityException if a security manager is installed and the548* caller does not have a549* {@link AuthPermission#AuthPermission(String)550* AuthPermission("doAsPrivileged")} permission to invoke551* this method.552*553* @deprecated This method is only useful in conjunction with554* {@linkplain SecurityManager the Security Manager}, which is555* deprecated and subject to removal in a future release.556* Consequently, this method is also deprecated and subject to557* removal. There is no replacement for the Security Manager or this558* method.559*/560@SuppressWarnings("removal")561@Deprecated(since="17", forRemoval=true)562public static <T> T doAsPrivileged(final Subject subject,563final java.security.PrivilegedExceptionAction<T> action,564final java.security.AccessControlContext acc)565throws java.security.PrivilegedActionException {566567java.lang.SecurityManager sm = System.getSecurityManager();568if (sm != null) {569sm.checkPermission(AuthPermissionHolder.DO_AS_PRIVILEGED_PERMISSION);570}571572Objects.requireNonNull(action,573ResourcesMgr.getString("invalid.null.action.provided"));574575// set up the new Subject-based AccessControlContext for doPrivileged576final AccessControlContext callerAcc =577(acc == null ?578new AccessControlContext(NULL_PD_ARRAY) :579acc);580581// call doPrivileged and push this new context on the stack582return java.security.AccessController.doPrivileged583(action,584createContext(subject, callerAcc));585}586587@SuppressWarnings("removal")588private static AccessControlContext createContext(final Subject subject,589final AccessControlContext acc) {590591592return java.security.AccessController.doPrivileged593(new java.security.PrivilegedAction<>() {594public AccessControlContext run() {595if (subject == null) {596return new AccessControlContext(acc, null);597} else {598return new AccessControlContext599(acc,600new SubjectDomainCombiner(subject));601}602}603});604}605606/**607* Return the {@code Set} of Principals associated with this608* {@code Subject}. Each {@code Principal} represents609* an identity for this {@code Subject}.610*611* <p> The returned {@code Set} is backed by this Subject's612* internal {@code Principal} {@code Set}. Any modification613* to the returned {@code Set} affects the internal614* {@code Principal} {@code Set} as well.615*616* <p> If a security manager is installed, the caller must have a617* {@link AuthPermission#AuthPermission(String)618* AuthPermission("modifyPrincipals")} permission to modify619* the returned set, or a {@code SecurityException} will be thrown.620*621* @return the {@code Set} of Principals associated with this622* {@code Subject}.623*/624public Set<Principal> getPrincipals() {625626// always return an empty Set instead of null627// so LoginModules can add to the Set if necessary628return principals;629}630631/**632* Return a {@code Set} of Principals associated with this633* {@code Subject} that are instances or subclasses of the specified634* {@code Class}.635*636* <p> The returned {@code Set} is not backed by this Subject's637* internal {@code Principal} {@code Set}. A new638* {@code Set} is created and returned for each method invocation.639* Modifications to the returned {@code Set}640* will not affect the internal {@code Principal} {@code Set}.641*642* @param <T> the type of the class modeled by {@code c}643*644* @param c the returned {@code Set} of Principals will all be645* instances of this class.646*647* @return a {@code Set} of Principals that are instances of the648* specified {@code Class}.649*650* @throws NullPointerException if the specified {@code Class}651* is {@code null}.652*/653public <T extends Principal> Set<T> getPrincipals(Class<T> c) {654655Objects.requireNonNull(c,656ResourcesMgr.getString("invalid.null.Class.provided"));657658// always return an empty Set instead of null659// so LoginModules can add to the Set if necessary660return new ClassSet<T>(PRINCIPAL_SET, c);661}662663/**664* Return the {@code Set} of public credentials held by this665* {@code Subject}.666*667* <p> The returned {@code Set} is backed by this Subject's668* internal public Credential {@code Set}. Any modification669* to the returned {@code Set} affects the internal public670* Credential {@code Set} as well.671*672* <p> If a security manager is installed, the caller must have a673* {@link AuthPermission#AuthPermission(String)674* AuthPermission("modifyPublicCredentials")} permission to modify675* the returned set, or a {@code SecurityException} will be thrown.676*677* @return a {@code Set} of public credentials held by this678* {@code Subject}.679*/680public Set<Object> getPublicCredentials() {681682// always return an empty Set instead of null683// so LoginModules can add to the Set if necessary684return pubCredentials;685}686687/**688* Return the {@code Set} of private credentials held by this689* {@code Subject}.690*691* <p> The returned {@code Set} is backed by this Subject's692* internal private Credential {@code Set}. Any modification693* to the returned {@code Set} affects the internal private694* Credential {@code Set} as well.695*696* <p> If a security manager is installed, the caller must have a697* {@link AuthPermission#AuthPermission(String)698* AuthPermission("modifyPrivateCredentials")} permission to modify699* the returned set, or a {@code SecurityException} will be thrown.700*701* <p> While iterating through the {@code Set},702* a {@code SecurityException} is thrown if a security manager is installed703* and the caller does not have a {@link PrivateCredentialPermission}704* to access a particular Credential. The {@code Iterator}705* is nevertheless advanced to the next element in the {@code Set}.706*707* @return a {@code Set} of private credentials held by this708* {@code Subject}.709*/710public Set<Object> getPrivateCredentials() {711712// XXX713// we do not need a security check for714// AuthPermission(getPrivateCredentials)715// because we already restrict access to private credentials716// via the PrivateCredentialPermission. all the extra AuthPermission717// would do is protect the set operations themselves718// (like size()), which don't seem security-sensitive.719720// always return an empty Set instead of null721// so LoginModules can add to the Set if necessary722return privCredentials;723}724725/**726* Return a {@code Set} of public credentials associated with this727* {@code Subject} that are instances or subclasses of the specified728* {@code Class}.729*730* <p> The returned {@code Set} is not backed by this Subject's731* internal public Credential {@code Set}. A new732* {@code Set} is created and returned for each method invocation.733* Modifications to the returned {@code Set}734* will not affect the internal public Credential {@code Set}.735*736* @param <T> the type of the class modeled by {@code c}737*738* @param c the returned {@code Set} of public credentials will all be739* instances of this class.740*741* @return a {@code Set} of public credentials that are instances742* of the specified {@code Class}.743*744* @throws NullPointerException if the specified {@code Class}745* is {@code null}.746*/747public <T> Set<T> getPublicCredentials(Class<T> c) {748749Objects.requireNonNull(c,750ResourcesMgr.getString("invalid.null.Class.provided"));751752// always return an empty Set instead of null753// so LoginModules can add to the Set if necessary754return new ClassSet<T>(PUB_CREDENTIAL_SET, c);755}756757/**758* Return a {@code Set} of private credentials associated with this759* {@code Subject} that are instances or subclasses of the specified760* {@code Class}.761*762* <p> If a security manager is installed, the caller must have a763* {@link PrivateCredentialPermission} to access all of the requested764* Credentials, or a {@code SecurityException} will be thrown.765*766* <p> The returned {@code Set} is not backed by this Subject's767* internal private Credential {@code Set}. A new768* {@code Set} is created and returned for each method invocation.769* Modifications to the returned {@code Set}770* will not affect the internal private Credential {@code Set}.771*772* @param <T> the type of the class modeled by {@code c}773*774* @param c the returned {@code Set} of private credentials will all be775* instances of this class.776*777* @return a {@code Set} of private credentials that are instances778* of the specified {@code Class}.779*780* @throws NullPointerException if the specified {@code Class}781* is {@code null}.782*/783public <T> Set<T> getPrivateCredentials(Class<T> c) {784785// XXX786// we do not need a security check for787// AuthPermission(getPrivateCredentials)788// because we already restrict access to private credentials789// via the PrivateCredentialPermission. all the extra AuthPermission790// would do is protect the set operations themselves791// (like size()), which don't seem security-sensitive.792793Objects.requireNonNull(c,794ResourcesMgr.getString("invalid.null.Class.provided"));795796// always return an empty Set instead of null797// so LoginModules can add to the Set if necessary798return new ClassSet<T>(PRIV_CREDENTIAL_SET, c);799}800801/**802* Compares the specified Object with this {@code Subject}803* for equality. Returns true if the given object is also a Subject804* and the two {@code Subject} instances are equivalent.805* More formally, two {@code Subject} instances are806* equal if their {@code Principal} and {@code Credential}807* Sets are equal.808*809* @param o Object to be compared for equality with this810* {@code Subject}.811*812* @return true if the specified Object is equal to this813* {@code Subject}.814*815* @throws SecurityException if a security manager is installed and the816* caller does not have a {@link PrivateCredentialPermission}817* permission to access the private credentials for this818* {@code Subject} or the provided {@code Subject}.819*/820@Override821public boolean equals(Object o) {822823if (o == null) {824return false;825}826827if (this == o) {828return true;829}830831if (o instanceof Subject) {832833final Subject that = (Subject)o;834835// check the principal and credential sets836Set<Principal> thatPrincipals;837synchronized(that.principals) {838// avoid deadlock from dual locks839thatPrincipals = new HashSet<>(that.principals);840}841if (!principals.equals(thatPrincipals)) {842return false;843}844845Set<Object> thatPubCredentials;846synchronized(that.pubCredentials) {847// avoid deadlock from dual locks848thatPubCredentials = new HashSet<>(that.pubCredentials);849}850if (!pubCredentials.equals(thatPubCredentials)) {851return false;852}853854Set<Object> thatPrivCredentials;855synchronized(that.privCredentials) {856// avoid deadlock from dual locks857thatPrivCredentials = new HashSet<>(that.privCredentials);858}859if (!privCredentials.equals(thatPrivCredentials)) {860return false;861}862return true;863}864return false;865}866867/**868* Return the String representation of this {@code Subject}.869*870* @return the String representation of this {@code Subject}.871*/872@Override873public String toString() {874return toString(true);875}876877/**878* package private convenience method to print out the Subject879* without firing off a security check when trying to access880* the Private Credentials881*/882String toString(boolean includePrivateCredentials) {883884String s = ResourcesMgr.getString("Subject.");885String suffix = "";886887synchronized(principals) {888Iterator<Principal> pI = principals.iterator();889while (pI.hasNext()) {890Principal p = pI.next();891suffix = suffix + ResourcesMgr.getString(".Principal.") +892p.toString() + ResourcesMgr.getString("NEWLINE");893}894}895896synchronized(pubCredentials) {897Iterator<Object> pI = pubCredentials.iterator();898while (pI.hasNext()) {899Object o = pI.next();900suffix = suffix +901ResourcesMgr.getString(".Public.Credential.") +902o.toString() + ResourcesMgr.getString("NEWLINE");903}904}905906if (includePrivateCredentials) {907synchronized(privCredentials) {908Iterator<Object> pI = privCredentials.iterator();909while (pI.hasNext()) {910try {911Object o = pI.next();912suffix += ResourcesMgr.getString913(".Private.Credential.") +914o.toString() +915ResourcesMgr.getString("NEWLINE");916} catch (SecurityException se) {917suffix += ResourcesMgr.getString918(".Private.Credential.inaccessible.");919break;920}921}922}923}924return s + suffix;925}926927/**928* Returns a hashcode for this {@code Subject}.929*930* @return a hashcode for this {@code Subject}.931*932* @throws SecurityException if a security manager is installed and the933* caller does not have a {@link PrivateCredentialPermission}934* permission to access this Subject's private credentials.935*/936@Override937public int hashCode() {938939/**940* The hashcode is derived exclusive or-ing the941* hashcodes of this Subject's Principals and credentials.942*943* If a particular credential was destroyed944* ({@code credential.hashCode()} throws an945* {@code IllegalStateException}),946* the hashcode for that credential is derived via:947* {@code credential.getClass().toString().hashCode()}.948*/949950int hashCode = 0;951952synchronized(principals) {953Iterator<Principal> pIterator = principals.iterator();954while (pIterator.hasNext()) {955Principal p = pIterator.next();956hashCode ^= p.hashCode();957}958}959960synchronized(pubCredentials) {961Iterator<Object> pubCIterator = pubCredentials.iterator();962while (pubCIterator.hasNext()) {963hashCode ^= getCredHashCode(pubCIterator.next());964}965}966return hashCode;967}968969/**970* get a credential's hashcode971*/972private int getCredHashCode(Object o) {973try {974return o.hashCode();975} catch (IllegalStateException ise) {976return o.getClass().toString().hashCode();977}978}979980/**981* Writes this object out to a stream (i.e., serializes it).982*983* @param oos the {@code ObjectOutputStream} to which data is written984* @throws IOException if an I/O error occurs985*/986@java.io.Serial987private void writeObject(java.io.ObjectOutputStream oos)988throws java.io.IOException {989synchronized(principals) {990oos.defaultWriteObject();991}992}993994/**995* Reads this object from a stream (i.e., deserializes it)996*997* @param s the {@code ObjectInputStream} from which data is read998* @throws IOException if an I/O error occurs999* @throws ClassNotFoundException if a serialized class cannot be loaded1000*/1001@SuppressWarnings("unchecked")1002@java.io.Serial1003private void readObject(java.io.ObjectInputStream s)1004throws java.io.IOException, ClassNotFoundException {10051006ObjectInputStream.GetField gf = s.readFields();10071008readOnly = gf.get("readOnly", false);10091010Set<Principal> inputPrincs = (Set<Principal>)gf.get("principals", null);10111012Objects.requireNonNull(inputPrincs,1013ResourcesMgr.getString("invalid.null.input.s."));10141015// Rewrap the principals into a SecureSet1016try {1017LinkedList<Principal> principalList = collectionNullClean(inputPrincs);1018principals = Collections.synchronizedSet(new SecureSet<>1019(this, PRINCIPAL_SET, principalList));1020} catch (NullPointerException npe) {1021// Sometimes people deserialize the principals set only.1022// Subject is not accessible, so just don't fail.1023principals = Collections.synchronizedSet1024(new SecureSet<>(this, PRINCIPAL_SET));1025}10261027// The Credential {@code Set} is not serialized, but we do not1028// want the default deserialization routine to set it to null.1029this.pubCredentials = Collections.synchronizedSet1030(new SecureSet<>(this, PUB_CREDENTIAL_SET));1031this.privCredentials = Collections.synchronizedSet1032(new SecureSet<>(this, PRIV_CREDENTIAL_SET));1033}10341035/**1036* Tests for null-clean collections (both non-null reference and1037* no null elements)1038*1039* @param coll A {@code Collection} to be tested for null references1040*1041* @throws NullPointerException if the specified collection is either1042* {@code null} or contains a {@code null} element1043*/1044private static <E> LinkedList<E> collectionNullClean(1045Collection<? extends E> coll) {10461047Objects.requireNonNull(coll,1048ResourcesMgr.getString("invalid.null.input.s."));10491050LinkedList<E> output = new LinkedList<>();1051for (E e : coll) {1052output.add(Objects.requireNonNull(e,1053ResourcesMgr.getString("invalid.null.input.s.")));1054}1055return output;1056}10571058/**1059* Prevent modifications unless caller has permission.1060*1061* @serial include1062*/1063private static class SecureSet<E>1064implements Set<E>, java.io.Serializable {10651066@java.io.Serial1067private static final long serialVersionUID = 7911754171111800359L;10681069/**1070* @serialField this$0 Subject The outer Subject instance.1071* @serialField elements LinkedList The elements in this set.1072*/1073@java.io.Serial1074private static final ObjectStreamField[] serialPersistentFields = {1075new ObjectStreamField("this$0", Subject.class),1076new ObjectStreamField("elements", LinkedList.class),1077new ObjectStreamField("which", int.class)1078};10791080Subject subject;1081LinkedList<E> elements;10821083/**1084* @serial An integer identifying the type of objects contained1085* in this set. If {@code which == 1},1086* this is a Principal set and all the elements are1087* of type {@code java.security.Principal}.1088* If {@code which == 2}, this is a public credential1089* set and all the elements are of type {@code Object}.1090* If {@code which == 3}, this is a private credential1091* set and all the elements are of type {@code Object}.1092*/1093private int which;10941095SecureSet(Subject subject, int which) {1096this.subject = subject;1097this.which = which;1098this.elements = new LinkedList<E>();1099}11001101SecureSet(Subject subject, int which, LinkedList<E> list) {1102this.subject = subject;1103this.which = which;1104this.elements = list;1105}11061107public int size() {1108return elements.size();1109}11101111public Iterator<E> iterator() {1112final LinkedList<E> list = elements;1113return new Iterator<E>() {1114ListIterator<E> i = list.listIterator(0);11151116public boolean hasNext() {return i.hasNext();}11171118public E next() {1119if (which != Subject.PRIV_CREDENTIAL_SET) {1120return i.next();1121}11221123@SuppressWarnings("removal")1124SecurityManager sm = System.getSecurityManager();1125if (sm != null) {1126try {1127sm.checkPermission(new PrivateCredentialPermission1128(list.get(i.nextIndex()).getClass().getName(),1129subject.getPrincipals()));1130} catch (SecurityException se) {1131i.next();1132throw (se);1133}1134}1135return i.next();1136}11371138public void remove() {11391140if (subject.isReadOnly()) {1141throw new IllegalStateException(ResourcesMgr.getString1142("Subject.is.read.only"));1143}11441145@SuppressWarnings("removal")1146java.lang.SecurityManager sm = System.getSecurityManager();1147if (sm != null) {1148switch (which) {1149case Subject.PRINCIPAL_SET:1150sm.checkPermission(AuthPermissionHolder.MODIFY_PRINCIPALS_PERMISSION);1151break;1152case Subject.PUB_CREDENTIAL_SET:1153sm.checkPermission(AuthPermissionHolder.MODIFY_PUBLIC_CREDENTIALS_PERMISSION);1154break;1155default:1156sm.checkPermission(AuthPermissionHolder.MODIFY_PRIVATE_CREDENTIALS_PERMISSION);1157break;1158}1159}1160i.remove();1161}1162};1163}11641165public boolean add(E o) {11661167Objects.requireNonNull(o,1168ResourcesMgr.getString("invalid.null.input.s."));11691170if (subject.isReadOnly()) {1171throw new IllegalStateException1172(ResourcesMgr.getString("Subject.is.read.only"));1173}11741175@SuppressWarnings("removal")1176java.lang.SecurityManager sm = System.getSecurityManager();1177if (sm != null) {1178switch (which) {1179case Subject.PRINCIPAL_SET:1180sm.checkPermission(AuthPermissionHolder.MODIFY_PRINCIPALS_PERMISSION);1181break;1182case Subject.PUB_CREDENTIAL_SET:1183sm.checkPermission(AuthPermissionHolder.MODIFY_PUBLIC_CREDENTIALS_PERMISSION);1184break;1185default:1186sm.checkPermission(AuthPermissionHolder.MODIFY_PRIVATE_CREDENTIALS_PERMISSION);1187break;1188}1189}11901191switch (which) {1192case Subject.PRINCIPAL_SET:1193if (!(o instanceof Principal)) {1194throw new SecurityException(ResourcesMgr.getString1195("attempting.to.add.an.object.which.is.not.an.instance.of.java.security.Principal.to.a.Subject.s.Principal.Set"));1196}1197break;1198default:1199// ok to add Objects of any kind to credential sets1200break;1201}12021203// check for duplicates1204if (!elements.contains(o))1205return elements.add(o);1206else {1207return false;1208}1209}12101211@SuppressWarnings("removal")1212public boolean remove(Object o) {12131214Objects.requireNonNull(o,1215ResourcesMgr.getString("invalid.null.input.s."));12161217final Iterator<E> e = iterator();1218while (e.hasNext()) {1219E next;1220if (which != Subject.PRIV_CREDENTIAL_SET) {1221next = e.next();1222} else {1223next = java.security.AccessController.doPrivileged1224(new java.security.PrivilegedAction<E>() {1225public E run() {1226return e.next();1227}1228});1229}12301231if (next.equals(o)) {1232e.remove();1233return true;1234}1235}1236return false;1237}12381239@SuppressWarnings("removal")1240public boolean contains(Object o) {12411242Objects.requireNonNull(o,1243ResourcesMgr.getString("invalid.null.input.s."));12441245final Iterator<E> e = iterator();1246while (e.hasNext()) {1247E next;1248if (which != Subject.PRIV_CREDENTIAL_SET) {1249next = e.next();1250} else {12511252// For private credentials:1253// If the caller does not have read permission for1254// for o.getClass(), we throw a SecurityException.1255// Otherwise we check the private cred set to see whether1256// it contains the Object12571258SecurityManager sm = System.getSecurityManager();1259if (sm != null) {1260sm.checkPermission(new PrivateCredentialPermission1261(o.getClass().getName(),1262subject.getPrincipals()));1263}1264next = java.security.AccessController.doPrivileged1265(new java.security.PrivilegedAction<E>() {1266public E run() {1267return e.next();1268}1269});1270}12711272if (next.equals(o)) {1273return true;1274}1275}1276return false;1277}12781279public boolean addAll(Collection<? extends E> c) {1280boolean result = false;12811282c = collectionNullClean(c);12831284for (E item : c) {1285result |= this.add(item);1286}12871288return result;1289}12901291@SuppressWarnings("removal")1292public boolean removeAll(Collection<?> c) {1293c = collectionNullClean(c);12941295boolean modified = false;1296final Iterator<E> e = iterator();1297while (e.hasNext()) {1298E next;1299if (which != Subject.PRIV_CREDENTIAL_SET) {1300next = e.next();1301} else {1302next = java.security.AccessController.doPrivileged1303(new java.security.PrivilegedAction<E>() {1304public E run() {1305return e.next();1306}1307});1308}13091310Iterator<?> ce = c.iterator();1311while (ce.hasNext()) {1312if (next.equals(ce.next())) {1313e.remove();1314modified = true;1315break;1316}1317}1318}1319return modified;1320}13211322public boolean containsAll(Collection<?> c) {1323c = collectionNullClean(c);13241325for (Object item : c) {1326if (this.contains(item) == false) {1327return false;1328}1329}13301331return true;1332}13331334@SuppressWarnings("removal")1335public boolean retainAll(Collection<?> c) {1336c = collectionNullClean(c);13371338boolean modified = false;1339final Iterator<E> e = iterator();1340while (e.hasNext()) {1341E next;1342if (which != Subject.PRIV_CREDENTIAL_SET) {1343next = e.next();1344} else {1345next = java.security.AccessController.doPrivileged1346(new java.security.PrivilegedAction<E>() {1347public E run() {1348return e.next();1349}1350});1351}13521353if (c.contains(next) == false) {1354e.remove();1355modified = true;1356}1357}13581359return modified;1360}13611362@SuppressWarnings("removal")1363public void clear() {1364final Iterator<E> e = iterator();1365while (e.hasNext()) {1366E next;1367if (which != Subject.PRIV_CREDENTIAL_SET) {1368next = e.next();1369} else {1370next = java.security.AccessController.doPrivileged1371(new java.security.PrivilegedAction<E>() {1372public E run() {1373return e.next();1374}1375});1376}1377e.remove();1378}1379}13801381public boolean isEmpty() {1382return elements.isEmpty();1383}13841385public Object[] toArray() {1386final Iterator<E> e = iterator();1387while (e.hasNext()) {1388// The next() method performs a security manager check1389// on each element in the SecureSet. If we make it all1390// the way through we should be able to simply return1391// element's toArray results. Otherwise we'll let1392// the SecurityException pass up the call stack.1393e.next();1394}13951396return elements.toArray();1397}13981399public <T> T[] toArray(T[] a) {1400final Iterator<E> e = iterator();1401while (e.hasNext()) {1402// The next() method performs a security manager check1403// on each element in the SecureSet. If we make it all1404// the way through we should be able to simply return1405// element's toArray results. Otherwise we'll let1406// the SecurityException pass up the call stack.1407e.next();1408}14091410return elements.toArray(a);1411}14121413public boolean equals(Object o) {1414if (o == this) {1415return true;1416}14171418if (!(o instanceof Set)) {1419return false;1420}14211422Collection<?> c = (Collection<?>) o;1423if (c.size() != size()) {1424return false;1425}14261427try {1428return containsAll(c);1429} catch (ClassCastException unused) {1430return false;1431} catch (NullPointerException unused) {1432return false;1433}1434}14351436public int hashCode() {1437int h = 0;1438Iterator<E> i = iterator();1439while (i.hasNext()) {1440E obj = i.next();1441if (obj != null) {1442h += obj.hashCode();1443}1444}1445return h;1446}14471448/**1449* Writes this object out to a stream (i.e., serializes it).1450*1451* @serialData If this is a private credential set,1452* a security check is performed to ensure that1453* the caller has permission to access each credential1454* in the set. If the security check passes,1455* the set is serialized.1456*1457* @param oos the {@code ObjectOutputStream} to which data is written1458* @throws IOException if an I/O error occurs1459*/1460@java.io.Serial1461private void writeObject(java.io.ObjectOutputStream oos)1462throws java.io.IOException {14631464if (which == Subject.PRIV_CREDENTIAL_SET) {1465// check permissions before serializing1466Iterator<E> i = iterator();1467while (i.hasNext()) {1468i.next();1469}1470}1471ObjectOutputStream.PutField fields = oos.putFields();1472fields.put("this$0", subject);1473fields.put("elements", elements);1474fields.put("which", which);1475oos.writeFields();1476}14771478/**1479* Restores the state of this object from the stream.1480*1481* @param ois the {@code ObjectInputStream} from which data is read1482* @throws IOException if an I/O error occurs1483* @throws ClassNotFoundException if a serialized class cannot be loaded1484*/1485@SuppressWarnings("unchecked")1486@java.io.Serial1487private void readObject(ObjectInputStream ois)1488throws IOException, ClassNotFoundException1489{1490ObjectInputStream.GetField fields = ois.readFields();1491subject = (Subject) fields.get("this$0", null);1492which = fields.get("which", 0);14931494LinkedList<E> tmp = (LinkedList<E>) fields.get("elements", null);14951496elements = Subject.collectionNullClean(tmp);1497}14981499}15001501/**1502* This class implements a {@code Set} which returns only1503* members that are an instance of a specified Class.1504*/1505private class ClassSet<T> extends AbstractSet<T> {15061507private int which;1508private Class<T> c;1509private Set<T> set;15101511ClassSet(int which, Class<T> c) {1512this.which = which;1513this.c = c;1514set = new HashSet<T>();15151516switch (which) {1517case Subject.PRINCIPAL_SET:1518synchronized(principals) { populateSet(); }1519break;1520case Subject.PUB_CREDENTIAL_SET:1521synchronized(pubCredentials) { populateSet(); }1522break;1523default:1524synchronized(privCredentials) { populateSet(); }1525break;1526}1527}15281529@SuppressWarnings({"removal","unchecked"}) /*To suppress warning from line 1374*/1530private void populateSet() {1531final Iterator<?> iterator;1532switch(which) {1533case Subject.PRINCIPAL_SET:1534iterator = Subject.this.principals.iterator();1535break;1536case Subject.PUB_CREDENTIAL_SET:1537iterator = Subject.this.pubCredentials.iterator();1538break;1539default:1540iterator = Subject.this.privCredentials.iterator();1541break;1542}15431544// Check whether the caller has permisson to get1545// credentials of Class c15461547while (iterator.hasNext()) {1548Object next;1549if (which == Subject.PRIV_CREDENTIAL_SET) {1550next = java.security.AccessController.doPrivileged1551(new java.security.PrivilegedAction<>() {1552public Object run() {1553return iterator.next();1554}1555});1556} else {1557next = iterator.next();1558}1559if (c.isAssignableFrom(next.getClass())) {1560if (which != Subject.PRIV_CREDENTIAL_SET) {1561set.add((T)next);1562} else {1563// Check permission for private creds1564SecurityManager sm = System.getSecurityManager();1565if (sm != null) {1566sm.checkPermission(new PrivateCredentialPermission1567(next.getClass().getName(),1568Subject.this.getPrincipals()));1569}1570set.add((T)next);1571}1572}1573}1574}15751576@Override1577public int size() {1578return set.size();1579}15801581@Override1582public Iterator<T> iterator() {1583return set.iterator();1584}15851586@Override1587public boolean add(T o) {15881589if (!c.isAssignableFrom(o.getClass())) {1590MessageFormat form = new MessageFormat(ResourcesMgr.getString1591("attempting.to.add.an.object.which.is.not.an.instance.of.class"));1592Object[] source = {c.toString()};1593throw new SecurityException(form.format(source));1594}15951596return set.add(o);1597}1598}15991600static final class AuthPermissionHolder {1601static final AuthPermission DO_AS_PERMISSION =1602new AuthPermission("doAs");16031604static final AuthPermission DO_AS_PRIVILEGED_PERMISSION =1605new AuthPermission("doAsPrivileged");16061607static final AuthPermission SET_READ_ONLY_PERMISSION =1608new AuthPermission("setReadOnly");16091610static final AuthPermission GET_SUBJECT_PERMISSION =1611new AuthPermission("getSubject");16121613static final AuthPermission MODIFY_PRINCIPALS_PERMISSION =1614new AuthPermission("modifyPrincipals");16151616static final AuthPermission MODIFY_PUBLIC_CREDENTIALS_PERMISSION =1617new AuthPermission("modifyPublicCredentials");16181619static final AuthPermission MODIFY_PRIVATE_CREDENTIALS_PERMISSION =1620new AuthPermission("modifyPrivateCredentials");1621}1622}162316241625