Home | History | Annotate | Download | only in auth
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package javax.security.auth;
     19 
     20 import java.io.IOException;
     21 import java.io.ObjectInputStream;
     22 import java.io.ObjectOutputStream;
     23 import java.io.Serializable;
     24 import java.security.AccessControlContext;
     25 import java.security.AccessController;
     26 import java.security.DomainCombiner;
     27 import java.security.Permission;
     28 import java.security.Principal;
     29 import java.security.PrivilegedAction;
     30 import java.security.PrivilegedActionException;
     31 import java.security.PrivilegedExceptionAction;
     32 import java.security.ProtectionDomain;
     33 import java.util.AbstractSet;
     34 import java.util.Collection;
     35 import java.util.Iterator;
     36 import java.util.LinkedList;
     37 import java.util.Set;
     38 
     39 /**
     40  * The central class of the {@code javax.security.auth} package representing an
     41  * authenticated user or entity (both referred to as "subject"). IT defines also
     42  * the static methods that allow code to be run, and do modifications according
     43  * to the subject's permissions.
     44  * <p>
     45  * A subject has the following features:
     46  * <ul>
     47  * <li>A set of {@code Principal} objects specifying the identities bound to a
     48  * {@code Subject} that distinguish it.</li>
     49  * <li>Credentials (public and private) such as certificates, keys, or
     50  * authentication proofs such as tickets</li>
     51  * </ul>
     52  */
     53 public final class Subject implements Serializable {
     54 
     55     private static final long serialVersionUID = -8308522755600156056L;
     56 
     57     private static final AuthPermission _AS = new AuthPermission("doAs");
     58 
     59     private static final AuthPermission _AS_PRIVILEGED = new AuthPermission(
     60             "doAsPrivileged");
     61 
     62     private static final AuthPermission _SUBJECT = new AuthPermission(
     63             "getSubject");
     64 
     65     private static final AuthPermission _PRINCIPALS = new AuthPermission(
     66             "modifyPrincipals");
     67 
     68     private static final AuthPermission _PRIVATE_CREDENTIALS = new AuthPermission(
     69             "modifyPrivateCredentials");
     70 
     71     private static final AuthPermission _PUBLIC_CREDENTIALS = new AuthPermission(
     72             "modifyPublicCredentials");
     73 
     74     private static final AuthPermission _READ_ONLY = new AuthPermission(
     75             "setReadOnly");
     76 
     77     private final Set<Principal> principals;
     78 
     79     private boolean readOnly;
     80 
     81     // set of private credentials
     82     private transient SecureSet<Object> privateCredentials;
     83 
     84     // set of public credentials
     85     private transient SecureSet<Object> publicCredentials;
     86 
     87     /**
     88      * The default constructor initializing the sets of public and private
     89      * credentials and principals with the empty set.
     90      */
     91     public Subject() {
     92         principals = new SecureSet<Principal>(_PRINCIPALS);
     93         publicCredentials = new SecureSet<Object>(_PUBLIC_CREDENTIALS);
     94         privateCredentials = new SecureSet<Object>(_PRIVATE_CREDENTIALS);
     95 
     96         readOnly = false;
     97     }
     98 
     99     /**
    100      * The constructor for the subject, setting its public and private
    101      * credentials and principals according to the arguments.
    102      *
    103      * @param readOnly
    104      *            {@code true} if this {@code Subject} is read-only, thus
    105      *            preventing any modifications to be done.
    106      * @param subjPrincipals
    107      *            the set of Principals that are attributed to this {@code
    108      *            Subject}.
    109      * @param pubCredentials
    110      *            the set of public credentials that distinguish this {@code
    111      *            Subject}.
    112      * @param privCredentials
    113      *            the set of private credentials that distinguish this {@code
    114      *            Subject}.
    115      */
    116     public Subject(boolean readOnly, Set<? extends Principal> subjPrincipals,
    117             Set<?> pubCredentials, Set<?> privCredentials) {
    118 
    119         if (subjPrincipals == null) {
    120             throw new NullPointerException("subjPrincipals == null");
    121         } else if (pubCredentials == null) {
    122             throw new NullPointerException("pubCredentials == null");
    123         } else if (privCredentials == null) {
    124             throw new NullPointerException("privCredentials == null");
    125         }
    126 
    127         principals = new SecureSet<Principal>(_PRINCIPALS, subjPrincipals);
    128         publicCredentials = new SecureSet<Object>(_PUBLIC_CREDENTIALS, pubCredentials);
    129         privateCredentials = new SecureSet<Object>(_PRIVATE_CREDENTIALS, privCredentials);
    130 
    131         this.readOnly = readOnly;
    132     }
    133 
    134     /**
    135      * Runs the code defined by {@code action} using the permissions granted to
    136      * the {@code Subject} itself and to the code as well.
    137      *
    138      * @param subject
    139      *            the distinguished {@code Subject}.
    140      * @param action
    141      *            the code to be run.
    142      * @return the {@code Object} returned when running the {@code action}.
    143      */
    144     @SuppressWarnings("unchecked")
    145     public static <T> T doAs(Subject subject, PrivilegedAction<T> action) {
    146         return doAs_PrivilegedAction(subject, action, AccessController.getContext());
    147     }
    148 
    149     /**
    150      * Run the code defined by {@code action} using the permissions granted to
    151      * the {@code Subject} and to the code itself, additionally providing a more
    152      * specific context.
    153      *
    154      * @param subject
    155      *            the distinguished {@code Subject}.
    156      * @param action
    157      *            the code to be run.
    158      * @param context
    159      *            the specific context in which the {@code action} is invoked.
    160      *            if {@code null} a new {@link AccessControlContext} is
    161      *            instantiated.
    162      * @return the {@code Object} returned when running the {@code action}.
    163      */
    164     @SuppressWarnings("unchecked")
    165     public static <T> T doAsPrivileged(Subject subject, PrivilegedAction<T> action,
    166             AccessControlContext context) {
    167         if (context == null) {
    168             return doAs_PrivilegedAction(subject, action, new AccessControlContext(
    169                     new ProtectionDomain[0]));
    170         }
    171         return doAs_PrivilegedAction(subject, action, context);
    172     }
    173 
    174     // instantiates a new context and passes it to AccessController
    175     @SuppressWarnings("unchecked")
    176     private static <T> T doAs_PrivilegedAction(Subject subject, PrivilegedAction<T> action,
    177             final AccessControlContext context) {
    178 
    179         AccessControlContext newContext;
    180 
    181         final SubjectDomainCombiner combiner;
    182         if (subject == null) {
    183             // performance optimization
    184             // if subject is null there is nothing to combine
    185             combiner = null;
    186         } else {
    187             combiner = new SubjectDomainCombiner(subject);
    188         }
    189 
    190         PrivilegedAction dccAction = new PrivilegedAction() {
    191             public Object run() {
    192                 return new AccessControlContext(context, combiner);
    193             }
    194         };
    195 
    196         newContext = (AccessControlContext) AccessController.doPrivileged(dccAction);
    197 
    198         return AccessController.doPrivileged(action, newContext);
    199     }
    200 
    201     /**
    202      * Runs the code defined by {@code action} using the permissions granted to
    203      * the subject and to the code itself.
    204      *
    205      * @param subject
    206      *            the distinguished {@code Subject}.
    207      * @param action
    208      *            the code to be run.
    209      * @return the {@code Object} returned when running the {@code action}.
    210      * @throws PrivilegedActionException
    211      *             if running the {@code action} throws an exception.
    212      */
    213     @SuppressWarnings("unchecked")
    214     public static <T> T doAs(Subject subject, PrivilegedExceptionAction<T> action)
    215             throws PrivilegedActionException {
    216         return doAs_PrivilegedExceptionAction(subject, action, AccessController.getContext());
    217     }
    218 
    219     /**
    220      * Runs the code defined by {@code action} using the permissions granted to
    221      * the subject and to the code itself, additionally providing a more
    222      * specific context.
    223      *
    224      * @param subject
    225      *            the distinguished {@code Subject}.
    226      * @param action
    227      *            the code to be run.
    228      * @param context
    229      *            the specific context in which the {@code action} is invoked.
    230      *            if {@code null} a new {@link AccessControlContext} is
    231      *            instantiated.
    232      * @return the {@code Object} returned when running the {@code action}.
    233      * @throws PrivilegedActionException
    234      *             if running the {@code action} throws an exception.
    235      */
    236     @SuppressWarnings("unchecked")
    237     public static <T> T doAsPrivileged(Subject subject,
    238             PrivilegedExceptionAction<T> action, AccessControlContext context)
    239             throws PrivilegedActionException {
    240         if (context == null) {
    241             return doAs_PrivilegedExceptionAction(subject, action,
    242                     new AccessControlContext(new ProtectionDomain[0]));
    243         }
    244         return doAs_PrivilegedExceptionAction(subject, action, context);
    245     }
    246 
    247     // instantiates a new context and passes it to AccessController
    248     @SuppressWarnings("unchecked")
    249     private static <T> T doAs_PrivilegedExceptionAction(Subject subject,
    250             PrivilegedExceptionAction<T> action, final AccessControlContext context)
    251             throws PrivilegedActionException {
    252 
    253         AccessControlContext newContext;
    254 
    255         final SubjectDomainCombiner combiner;
    256         if (subject == null) {
    257             // performance optimization
    258             // if subject is null there is nothing to combine
    259             combiner = null;
    260         } else {
    261             combiner = new SubjectDomainCombiner(subject);
    262         }
    263 
    264         PrivilegedAction<AccessControlContext> dccAction = new PrivilegedAction<AccessControlContext>() {
    265             public AccessControlContext run() {
    266                 return new AccessControlContext(context, combiner);
    267             }
    268         };
    269 
    270         newContext = AccessController.doPrivileged(dccAction);
    271 
    272         return AccessController.doPrivileged(action, newContext);
    273     }
    274 
    275     /**
    276      * Checks two Subjects for equality. More specifically if the principals,
    277      * public and private credentials are equal, equality for two {@code
    278      * Subjects} is implied.
    279      *
    280      * @param obj
    281      *            the {@code Object} checked for equality with this {@code
    282      *            Subject}.
    283      * @return {@code true} if the specified {@code Subject} is equal to this
    284      *         one.
    285      */
    286     @Override
    287     public boolean equals(Object obj) {
    288 
    289         if (this == obj) {
    290             return true;
    291         }
    292 
    293         if (obj == null || this.getClass() != obj.getClass()) {
    294             return false;
    295         }
    296 
    297         Subject that = (Subject) obj;
    298 
    299         if (principals.equals(that.principals)
    300                 && publicCredentials.equals(that.publicCredentials)
    301                 && privateCredentials.equals(that.privateCredentials)) {
    302             return true;
    303         }
    304         return false;
    305     }
    306 
    307     /**
    308      * Returns this {@code Subject}'s {@link Principal}.
    309      *
    310      * @return this {@code Subject}'s {@link Principal}.
    311      */
    312     public Set<Principal> getPrincipals() {
    313         return principals;
    314     }
    315 
    316 
    317     /**
    318      * Returns this {@code Subject}'s {@link Principal} which is a subclass of
    319      * the {@code Class} provided.
    320      *
    321      * @param c
    322      *            the {@code Class} as a criteria which the {@code Principal}
    323      *            returned must satisfy.
    324      * @return this {@code Subject}'s {@link Principal}. Modifications to the
    325      *         returned set of {@code Principal}s do not affect this {@code
    326      *         Subject}'s set.
    327      */
    328     public <T extends Principal> Set<T> getPrincipals(Class<T> c) {
    329         return ((SecureSet<Principal>) principals).get(c);
    330     }
    331 
    332     /**
    333      * Returns the private credentials associated with this {@code Subject}.
    334      *
    335      * @return the private credentials associated with this {@code Subject}.
    336      */
    337     public Set<Object> getPrivateCredentials() {
    338         return privateCredentials;
    339     }
    340 
    341     /**
    342      * Returns this {@code Subject}'s private credentials which are a subclass
    343      * of the {@code Class} provided.
    344      *
    345      * @param c
    346      *            the {@code Class} as a criteria which the private credentials
    347      *            returned must satisfy.
    348      * @return this {@code Subject}'s private credentials. Modifications to the
    349      *         returned set of credentials do not affect this {@code Subject}'s
    350      *         credentials.
    351      */
    352     public <T> Set<T> getPrivateCredentials(Class<T> c) {
    353         return privateCredentials.get(c);
    354     }
    355 
    356     /**
    357      * Returns the public credentials associated with this {@code Subject}.
    358      *
    359      * @return the public credentials associated with this {@code Subject}.
    360      */
    361     public Set<Object> getPublicCredentials() {
    362         return publicCredentials;
    363     }
    364 
    365 
    366     /**
    367      * Returns this {@code Subject}'s public credentials which are a subclass of
    368      * the {@code Class} provided.
    369      *
    370      * @param c
    371      *            the {@code Class} as a criteria which the public credentials
    372      *            returned must satisfy.
    373      * @return this {@code Subject}'s public credentials. Modifications to the
    374      *         returned set of credentials do not affect this {@code Subject}'s
    375      *         credentials.
    376      */
    377     public <T> Set<T> getPublicCredentials(Class<T> c) {
    378         return publicCredentials.get(c);
    379     }
    380 
    381     /**
    382      * Returns a hash code of this {@code Subject}.
    383      *
    384      * @return a hash code of this {@code Subject}.
    385      */
    386     @Override
    387     public int hashCode() {
    388         return principals.hashCode() + privateCredentials.hashCode()
    389                 + publicCredentials.hashCode();
    390     }
    391 
    392     /**
    393      * Prevents from modifications being done to the credentials and {@link
    394      * Principal} sets. After setting it to read-only this {@code Subject} can
    395      * not be made writable again. The destroy method on the credentials still
    396      * works though.
    397      */
    398     public void setReadOnly() {
    399         readOnly = true;
    400     }
    401 
    402     /**
    403      * Returns whether this {@code Subject} is read-only or not.
    404      *
    405      * @return whether this {@code Subject} is read-only or not.
    406      */
    407     public boolean isReadOnly() {
    408         return readOnly;
    409     }
    410 
    411     /**
    412      * Returns a {@code String} representation of this {@code Subject}.
    413      *
    414      * @return a {@code String} representation of this {@code Subject}.
    415      */
    416     @Override
    417     public String toString() {
    418         StringBuilder buf = new StringBuilder("Subject:\n");
    419 
    420         Iterator<?> it = principals.iterator();
    421         while (it.hasNext()) {
    422             buf.append("\tPrincipal: ");
    423             buf.append(it.next());
    424             buf.append('\n');
    425         }
    426 
    427         it = publicCredentials.iterator();
    428         while (it.hasNext()) {
    429             buf.append("\tPublic Credential: ");
    430             buf.append(it.next());
    431             buf.append('\n');
    432         }
    433 
    434         int offset = buf.length() - 1;
    435         it = privateCredentials.iterator();
    436         try {
    437             while (it.hasNext()) {
    438                 buf.append("\tPrivate Credential: ");
    439                 buf.append(it.next());
    440                 buf.append('\n');
    441             }
    442         } catch (SecurityException e) {
    443             buf.delete(offset, buf.length());
    444             buf.append("\tPrivate Credentials: no accessible information\n");
    445         }
    446         return buf.toString();
    447     }
    448 
    449     private void readObject(ObjectInputStream in) throws IOException,
    450             ClassNotFoundException {
    451 
    452         in.defaultReadObject();
    453 
    454         publicCredentials = new SecureSet<Object>(_PUBLIC_CREDENTIALS);
    455         privateCredentials = new SecureSet<Object>(_PRIVATE_CREDENTIALS);
    456     }
    457 
    458     private void writeObject(ObjectOutputStream out) throws IOException {
    459         out.defaultWriteObject();
    460     }
    461 
    462     /**
    463      * Returns the {@code Subject} that was last associated with the {@code
    464      * context} provided as argument.
    465      *
    466      * @param context
    467      *            the {@code context} that was associated with the
    468      *            {@code Subject}.
    469      * @return the {@code Subject} that was last associated with the {@code
    470      *         context} provided as argument.
    471      */
    472     public static Subject getSubject(final AccessControlContext context) {
    473         if (context == null) {
    474             throw new NullPointerException("context == null");
    475         }
    476         PrivilegedAction<DomainCombiner> action = new PrivilegedAction<DomainCombiner>() {
    477             public DomainCombiner run() {
    478                 return context.getDomainCombiner();
    479             }
    480         };
    481         DomainCombiner combiner = AccessController.doPrivileged(action);
    482 
    483         if ((combiner == null) || !(combiner instanceof SubjectDomainCombiner)) {
    484             return null;
    485         }
    486         return ((SubjectDomainCombiner) combiner).getSubject();
    487     }
    488 
    489     private void checkState() {
    490         if (readOnly) {
    491             throw new IllegalStateException("Set is read-only");
    492         }
    493     }
    494 
    495     private final class SecureSet<SST> extends AbstractSet<SST> implements Serializable {
    496 
    497         /**
    498          * Compatibility issue: see comments for setType variable
    499          */
    500         private static final long serialVersionUID = 7911754171111800359L;
    501 
    502         private LinkedList<SST> elements;
    503 
    504         /*
    505          * Is used to define a set type for serialization.
    506          *
    507          * A type can be principal, priv. or pub. credential set. The spec.
    508          * doesn't clearly says that priv. and pub. credential sets can be
    509          * serialized and what classes they are. It is only possible to figure
    510          * out from writeObject method comments that priv. credential set is
    511          * serializable and it is an instance of SecureSet class. So pub.
    512          * credential was implemented by analogy
    513          *
    514          * Compatibility issue: the class follows its specified serial form.
    515          * Also according to the serialization spec. adding new field is a
    516          * compatible change. So is ok for principal set (because the default
    517          * value for integer is zero). But priv. or pub. credential set it is
    518          * not compatible because most probably other implementations resolve
    519          * this issue in other way
    520          */
    521         private int setType;
    522 
    523         // Defines principal set for serialization.
    524         private static final int SET_Principal = 0;
    525 
    526         // Defines private credential set for serialization.
    527         private static final int SET_PrivCred = 1;
    528 
    529         // Defines public credential set for serialization.
    530         private static final int SET_PubCred = 2;
    531 
    532         // permission required to modify set
    533         private transient AuthPermission permission;
    534 
    535         protected SecureSet(AuthPermission perm) {
    536             permission = perm;
    537             elements = new LinkedList<SST>();
    538         }
    539 
    540         // creates set from specified collection with specified permission
    541         // all collection elements are verified before adding
    542         protected SecureSet(AuthPermission perm, Collection<? extends SST> s) {
    543             this(perm);
    544 
    545             // Subject's constructor receives a Set, we can trusts if a set is from bootclasspath,
    546             // and not to check whether it contains duplicates or not
    547             boolean trust = s.getClass().getClassLoader() == null;
    548 
    549             for (SST o : s) {
    550                 verifyElement(o);
    551                 if (trust || !elements.contains(o)) {
    552                     elements.add(o);
    553                 }
    554             }
    555         }
    556 
    557         // verifies new set element
    558         private void verifyElement(Object o) {
    559 
    560             if (o == null) {
    561                 throw new NullPointerException("o == null");
    562             }
    563             if (permission == _PRINCIPALS && !(Principal.class.isAssignableFrom(o.getClass()))) {
    564                 throw new IllegalArgumentException("Element is not instance of java.security.Principal");
    565             }
    566         }
    567 
    568         /*
    569          * verifies specified element, checks set state, and security permission
    570          * to modify set before adding new element
    571          */
    572         @Override
    573         public boolean add(SST o) {
    574 
    575             verifyElement(o);
    576 
    577             checkState();
    578 
    579             if (!elements.contains(o)) {
    580                 elements.add(o);
    581                 return true;
    582             }
    583             return false;
    584         }
    585 
    586         // returns an instance of SecureIterator
    587         @Override
    588         public Iterator<SST> iterator() {
    589 
    590             if (permission == _PRIVATE_CREDENTIALS) {
    591                 /*
    592                  * private credential set requires iterator with additional
    593                  * security check (PrivateCredentialPermission)
    594                  */
    595                 return new SecureIterator(elements.iterator()) {
    596                     /*
    597                      * checks permission to access next private credential moves
    598                      * to the next element even SecurityException was thrown
    599                      */
    600                     @Override
    601                     public SST next() {
    602                         SST obj = iterator.next();
    603                         return obj;
    604                     }
    605                 };
    606             }
    607             return new SecureIterator(elements.iterator());
    608         }
    609 
    610         @Override
    611         public boolean retainAll(Collection<?> c) {
    612 
    613             if (c == null) {
    614                 throw new NullPointerException("c == null");
    615             }
    616             return super.retainAll(c);
    617         }
    618 
    619         @Override
    620         public int size() {
    621             return elements.size();
    622         }
    623 
    624         /**
    625          * return set with elements that are instances or subclasses of the
    626          * specified class
    627          */
    628         protected final <E> Set<E> get(final Class<E> c) {
    629 
    630             if (c == null) {
    631                 throw new NullPointerException("c == null");
    632             }
    633 
    634             AbstractSet<E> s = new AbstractSet<E>() {
    635                 private LinkedList<E> elements = new LinkedList<E>();
    636 
    637                 @Override
    638                 public boolean add(E o) {
    639                     if (!c.isAssignableFrom(o.getClass())) {
    640                         throw new IllegalArgumentException("Invalid type: " + o.getClass());
    641                     }
    642 
    643                     if (elements.contains(o)) {
    644                         return false;
    645                     }
    646                     elements.add(o);
    647                     return true;
    648                 }
    649 
    650                 @Override
    651                 public Iterator<E> iterator() {
    652                     return elements.iterator();
    653                 }
    654 
    655                 @Override
    656                 public boolean retainAll(Collection<?> c) {
    657 
    658                     if (c == null) {
    659                         throw new NullPointerException("c == null");
    660                     }
    661                     return super.retainAll(c);
    662                 }
    663 
    664                 @Override
    665                 public int size() {
    666                     return elements.size();
    667                 }
    668             };
    669 
    670             // FIXME must have permissions for requested priv. credentials
    671             for (SST o : this) {
    672                 if (c.isAssignableFrom(o.getClass())) {
    673                     s.add(c.cast(o));
    674                 }
    675             }
    676             return s;
    677         }
    678 
    679         private void readObject(ObjectInputStream in) throws IOException,
    680                 ClassNotFoundException {
    681             in.defaultReadObject();
    682 
    683             switch (setType) {
    684             case SET_Principal:
    685                 permission = _PRINCIPALS;
    686                 break;
    687             case SET_PrivCred:
    688                 permission = _PRIVATE_CREDENTIALS;
    689                 break;
    690             case SET_PubCred:
    691                 permission = _PUBLIC_CREDENTIALS;
    692                 break;
    693             default:
    694                 throw new IllegalArgumentException();
    695             }
    696 
    697             for (SST element : elements) {
    698                 verifyElement(element);
    699             }
    700         }
    701 
    702         @SuppressWarnings("UnusedDeclaration")
    703         private void writeObject(ObjectOutputStream out) throws IOException {
    704 
    705             if (permission == _PRIVATE_CREDENTIALS) {
    706                 setType = SET_PrivCred;
    707             } else if (permission == _PRINCIPALS) {
    708                 setType = SET_Principal;
    709             } else {
    710                 setType = SET_PubCred;
    711             }
    712 
    713             out.defaultWriteObject();
    714         }
    715 
    716         /**
    717          * Represents iterator for subject's secure set
    718          */
    719         private class SecureIterator implements Iterator<SST> {
    720             protected Iterator<SST> iterator;
    721 
    722             protected SecureIterator(Iterator<SST> iterator) {
    723                 this.iterator = iterator;
    724             }
    725 
    726             public boolean hasNext() {
    727                 return iterator.hasNext();
    728             }
    729 
    730             public SST next() {
    731                 return iterator.next();
    732             }
    733 
    734             /**
    735              * checks set state, and security permission to modify set before
    736              * removing current element
    737              */
    738             public void remove() {
    739                 checkState();
    740                 iterator.remove();
    741             }
    742         }
    743     }
    744 }
    745