Save This Page
Home » openjdk-7 » javax.security » auth » login » [javadoc | source]
    1   /*
    2    * Copyright 1998-2006 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package javax.security.auth.login;
   27   
   28   import java.lang.reflect.Constructor;
   29   import java.lang.reflect.Method;
   30   import java.lang.reflect.InvocationTargetException;
   31   import java.util.LinkedList;
   32   import java.util.Map;
   33   import java.util.HashMap;
   34   import java.text.MessageFormat;
   35   import javax.security.auth.Subject;
   36   import javax.security.auth.AuthPermission;
   37   import javax.security.auth.callback;
   38   import java.security.AccessController;
   39   import java.security.AccessControlContext;
   40   import sun.security.util.PendingException;
   41   import sun.security.util.ResourcesMgr;
   42   
   43   /**
   44    * <p> The <code>LoginContext</code> class describes the basic methods used
   45    * to authenticate Subjects and provides a way to develop an
   46    * application independent of the underlying authentication technology.
   47    * A <code>Configuration</code> specifies the authentication technology, or
   48    * <code>LoginModule</code>, to be used with a particular application.
   49    * Different LoginModules can be plugged in under an application
   50    * without requiring any modifications to the application itself.
   51    *
   52    * <p> In addition to supporting <i>pluggable</i> authentication, this class
   53    * also supports the notion of <i>stacked</i> authentication.
   54    * Applications may be configured to use more than one
   55    * LoginModule.  For example, one could
   56    * configure both a Kerberos LoginModule and a smart card
   57    * LoginModule under an application.
   58    *
   59    * <p> A typical caller instantiates a LoginContext with
   60    * a <i>name</i> and a <code>CallbackHandler</code>.
   61    * LoginContext uses the <i>name</i> as the index into a
   62    * Configuration to determine which LoginModules should be used,
   63    * and which ones must succeed in order for the overall authentication to
   64    * succeed.  The <code>CallbackHandler</code> is passed to the underlying
   65    * LoginModules so they may communicate and interact with users
   66    * (prompting for a username and password via a graphical user interface,
   67    * for example).
   68    *
   69    * <p> Once the caller has instantiated a LoginContext,
   70    * it invokes the <code>login</code> method to authenticate
   71    * a <code>Subject</code>.  The <code>login</code> method invokes
   72    * the configured modules to perform their respective types of authentication
   73    * (username/password, smart card pin verification, etc.).
   74    * Note that the LoginModules will not attempt authentication retries nor
   75    * introduce delays if the authentication fails.
   76    * Such tasks belong to the LoginContext caller.
   77    *
   78    * <p> If the <code>login</code> method returns without
   79    * throwing an exception, then the overall authentication succeeded.
   80    * The caller can then retrieve
   81    * the newly authenticated Subject by invoking the
   82    * <code>getSubject</code> method.  Principals and Credentials associated
   83    * with the Subject may be retrieved by invoking the Subject's
   84    * respective <code>getPrincipals</code>, <code>getPublicCredentials</code>,
   85    * and <code>getPrivateCredentials</code> methods.
   86    *
   87    * <p> To logout the Subject, the caller calls
   88    * the <code>logout</code> method.  As with the <code>login</code>
   89    * method, this <code>logout</code> method invokes the <code>logout</code>
   90    * method for the configured modules.
   91    *
   92    * <p> A LoginContext should not be used to authenticate
   93    * more than one Subject.  A separate LoginContext
   94    * should be used to authenticate each different Subject.
   95    *
   96    * <p> The following documentation applies to all LoginContext constructors:
   97    * <ol>
   98    *
   99    * <li> <code>Subject</code>
  100    * <ul>
  101    * <li> If the constructor has a Subject
  102    * input parameter, the LoginContext uses the caller-specified
  103    * Subject object.
  104    * <p>
  105    * <li> If the caller specifies a <code>null</code> Subject
  106    * and a <code>null</code> value is permitted,
  107    * the LoginContext instantiates a new Subject.
  108    * <p>
  109    * <li> If the constructor does <b>not</b> have a Subject
  110    * input parameter, the LoginContext instantiates a new Subject.
  111    * <p>
  112    * </ul>
  113    *
  114    * <li> <code>Configuration</code>
  115    * <ul>
  116    * <li> If the constructor has a Configuration
  117    * input parameter and the caller specifies a non-null Configuration,
  118    * the LoginContext uses the caller-specified Configuration.
  119    * <p>
  120    * If the constructor does <b>not</b> have a Configuration
  121    * input parameter, or if the caller specifies a <code>null</code>
  122    * Configuration object, the constructor uses the following call to
  123    * get the installed Configuration:
  124    * <pre>
  125    *      config = Configuration.getConfiguration();
  126    * </pre>
  127    * For both cases,
  128    * the <i>name</i> argument given to the constructor is passed to the
  129    * <code>Configuration.getAppConfigurationEntry</code> method.
  130    * If the Configuration has no entries for the specified <i>name</i>,
  131    * then the <code>LoginContext</code> calls
  132    * <code>getAppConfigurationEntry</code> with the name, "<i>other</i>"
  133    * (the default entry name).  If there is no entry for "<i>other</i>",
  134    * then a <code>LoginException</code> is thrown.
  135    * <p>
  136    * <li> When LoginContext uses the installed Configuration, the caller
  137    * requires the createLoginContext.<em>name</em> and possibly
  138    * createLoginContext.other AuthPermissions. Furthermore, the
  139    * LoginContext will invoke configured modules from within an
  140    * <code>AccessController.doPrivileged</code> call so that modules that
  141    * perform security-sensitive tasks (such as connecting to remote hosts,
  142    * and updating the Subject) will require the respective permissions, but
  143    * the callers of the LoginContext will not require those permissions.
  144    * <p>
  145    * <li> When LoginContext uses a caller-specified Configuration, the caller
  146    * does not require any createLoginContext AuthPermission.  The LoginContext
  147    * saves the <code>AccessControlContext</code> for the caller,
  148    * and invokes the configured modules from within an
  149    * <tt>AccessController.doPrivileged</tt> call constrained by that context.
  150    * This means the caller context (stored when the LoginContext was created)
  151    * must have sufficient permissions to perform any security-sensitive tasks
  152    * that the modules may perform.
  153    * <p>
  154    * </ul>
  155    *
  156    * <li> <code>CallbackHandler</code>
  157    * <ul>
  158    * <li> If the constructor has a CallbackHandler
  159    * input parameter, the LoginContext uses the caller-specified
  160    * CallbackHandler object.
  161    * <p>
  162    * <li> If the constructor does <b>not</b> have a CallbackHandler
  163    * input parameter, or if the caller specifies a <code>null</code>
  164    * CallbackHandler object (and a <code>null</code> value is permitted),
  165    * the LoginContext queries the
  166    * <i>auth.login.defaultCallbackHandler</i> security property
  167    * for the fully qualified class name of a default handler implementation.
  168    * If the security property is not set,
  169    * then the underlying modules will not have a
  170    * CallbackHandler for use in communicating
  171    * with users.  The caller thus assumes that the configured
  172    * modules have alternative means for authenticating the user.
  173    *
  174    * <p>
  175    * <li> When the LoginContext uses the installed Configuration (instead of
  176    * a caller-specified Configuration, see above),
  177    * then this LoginContext must wrap any
  178    * caller-specified or default CallbackHandler implementation
  179    * in a new CallbackHandler implementation
  180    * whose <code>handle</code> method implementation invokes the
  181    * specified CallbackHandler's <code>handle</code> method in a
  182    * <code>java.security.AccessController.doPrivileged</code> call
  183    * constrained by the caller's current <code>AccessControlContext</code>.
  184    * </ul>
  185    * </ol>
  186    *
  187    * <p> Note that Security Properties
  188    * (such as <code>auth.login.defaultCallbackHandler</code>)
  189    * can be set programmatically via the
  190    * <code>java.security.Security</code> class,
  191    * or statically in the Java security properties file located in the
  192    * file named &lt;JAVA_HOME&gt;/lib/security/java.security.
  193    * &lt;JAVA_HOME&gt; refers to the value of the java.home system property,
  194    * and specifies the directory where the JRE is installed.
  195    *
  196    * @see java.security.Security
  197    * @see javax.security.auth.AuthPermission
  198    * @see javax.security.auth.Subject
  199    * @see javax.security.auth.callback.CallbackHandler
  200    * @see javax.security.auth.login.Configuration
  201    * @see javax.security.auth.spi.LoginModule
  202    */
  203   public class LoginContext {
  204   
  205       private static final String INIT_METHOD             = "initialize";
  206       private static final String LOGIN_METHOD            = "login";
  207       private static final String COMMIT_METHOD           = "commit";
  208       private static final String ABORT_METHOD            = "abort";
  209       private static final String LOGOUT_METHOD           = "logout";
  210       private static final String OTHER                   = "other";
  211       private static final String DEFAULT_HANDLER         =
  212                                   "auth.login.defaultCallbackHandler";
  213       private Subject subject = null;
  214       private boolean subjectProvided = false;
  215       private boolean loginSucceeded = false;
  216       private CallbackHandler callbackHandler;
  217       private Map state = new HashMap();
  218   
  219       private Configuration config;
  220       private boolean configProvided = false;
  221       private AccessControlContext creatorAcc = null;
  222       private ModuleInfo[] moduleStack;
  223       private ClassLoader contextClassLoader = null;
  224       private static final Class[] PARAMS = { };
  225   
  226       // state saved in the event a user-specified asynchronous exception
  227       // was specified and thrown
  228   
  229       private int moduleIndex = 0;
  230       private LoginException firstError = null;
  231       private LoginException firstRequiredError = null;
  232       private boolean success = false;
  233   
  234       private static final sun.security.util.Debug debug =
  235           sun.security.util.Debug.getInstance("logincontext", "\t[LoginContext]");
  236   
  237       private void init(String name) throws LoginException {
  238   
  239           SecurityManager sm = System.getSecurityManager();
  240           if (sm != null && !configProvided) {
  241               sm.checkPermission(new AuthPermission
  242                                   ("createLoginContext." + name));
  243           }
  244   
  245           if (name == null)
  246               throw new LoginException
  247                   (ResourcesMgr.getString("Invalid null input: name"));
  248   
  249           // get the Configuration
  250           if (config == null) {
  251               config = java.security.AccessController.doPrivileged
  252                   (new java.security.PrivilegedAction<Configuration>() {
  253                   public Configuration run() {
  254                       return Configuration.getConfiguration();
  255                   }
  256               });
  257           }
  258   
  259           // get the LoginModules configured for this application
  260           AppConfigurationEntry[] entries = config.getAppConfigurationEntry(name);
  261           if (entries == null) {
  262   
  263               if (sm != null && !configProvided) {
  264                   sm.checkPermission(new AuthPermission
  265                                   ("createLoginContext." + OTHER));
  266               }
  267   
  268               entries = config.getAppConfigurationEntry(OTHER);
  269               if (entries == null) {
  270                   MessageFormat form = new MessageFormat(ResourcesMgr.getString
  271                           ("No LoginModules configured for name"));
  272                   Object[] source = {name};
  273                   throw new LoginException(form.format(source));
  274               }
  275           }
  276           moduleStack = new ModuleInfo[entries.length];
  277           for (int i = 0; i < entries.length; i++) {
  278               // clone returned array
  279               moduleStack[i] = new ModuleInfo
  280                                   (new AppConfigurationEntry
  281                                           (entries[i].getLoginModuleName(),
  282                                           entries[i].getControlFlag(),
  283                                           entries[i].getOptions()),
  284                                   null);
  285           }
  286   
  287           contextClassLoader = java.security.AccessController.doPrivileged
  288                   (new java.security.PrivilegedAction<ClassLoader>() {
  289                   public ClassLoader run() {
  290                       return Thread.currentThread().getContextClassLoader();
  291                   }
  292           });
  293       }
  294   
  295       private void loadDefaultCallbackHandler() throws LoginException {
  296   
  297           // get the default handler class
  298           try {
  299   
  300               final ClassLoader finalLoader = contextClassLoader;
  301   
  302               this.callbackHandler = java.security.AccessController.doPrivileged(
  303                   new java.security.PrivilegedExceptionAction<CallbackHandler>() {
  304                   public CallbackHandler run() throws Exception {
  305                       String defaultHandler = java.security.Security.getProperty
  306                           (DEFAULT_HANDLER);
  307                       if (defaultHandler == null || defaultHandler.length() == 0)
  308                           return null;
  309                       Class c = Class.forName(defaultHandler,
  310                                           true,
  311                                           finalLoader);
  312                       return (CallbackHandler)c.newInstance();
  313                   }
  314               });
  315           } catch (java.security.PrivilegedActionException pae) {
  316               throw new LoginException(pae.getException().toString());
  317           }
  318   
  319           // secure it with the caller's ACC
  320           if (this.callbackHandler != null && !configProvided) {
  321               this.callbackHandler = new SecureCallbackHandler
  322                                   (java.security.AccessController.getContext(),
  323                                   this.callbackHandler);
  324           }
  325       }
  326   
  327       /**
  328        * Instantiate a new <code>LoginContext</code> object with a name.
  329        *
  330        * @param name the name used as the index into the
  331        *          <code>Configuration</code>.
  332        *
  333        * @exception LoginException if the caller-specified <code>name</code>
  334        *          does not appear in the <code>Configuration</code>
  335        *          and there is no <code>Configuration</code> entry
  336        *          for "<i>other</i>", or if the
  337        *          <i>auth.login.defaultCallbackHandler</i>
  338        *          security property was set, but the implementation
  339        *          class could not be loaded.
  340        *          <p>
  341        * @exception SecurityException if a SecurityManager is set and
  342        *          the caller does not have
  343        *          AuthPermission("createLoginContext.<i>name</i>"),
  344        *          or if a configuration entry for <i>name</i> does not exist and
  345        *          the caller does not additionally have
  346        *          AuthPermission("createLoginContext.other")
  347        */
  348       public LoginContext(String name) throws LoginException {
  349           init(name);
  350           loadDefaultCallbackHandler();
  351       }
  352   
  353       /**
  354        * Instantiate a new <code>LoginContext</code> object with a name
  355        * and a <code>Subject</code> object.
  356        *
  357        * <p>
  358        *
  359        * @param name the name used as the index into the
  360        *          <code>Configuration</code>. <p>
  361        *
  362        * @param subject the <code>Subject</code> to authenticate.
  363        *
  364        * @exception LoginException if the caller-specified <code>name</code>
  365        *          does not appear in the <code>Configuration</code>
  366        *          and there is no <code>Configuration</code> entry
  367        *          for "<i>other</i>", if the caller-specified <code>subject</code>
  368        *          is <code>null</code>, or if the
  369        *          <i>auth.login.defaultCallbackHandler</i>
  370        *          security property was set, but the implementation
  371        *          class could not be loaded.
  372        *          <p>
  373        * @exception SecurityException if a SecurityManager is set and
  374        *          the caller does not have
  375        *          AuthPermission("createLoginContext.<i>name</i>"),
  376        *          or if a configuration entry for <i>name</i> does not exist and
  377        *          the caller does not additionally have
  378        *          AuthPermission("createLoginContext.other")
  379        */
  380       public LoginContext(String name, Subject subject)
  381       throws LoginException {
  382           init(name);
  383           if (subject == null)
  384               throw new LoginException
  385                   (ResourcesMgr.getString("invalid null Subject provided"));
  386           this.subject = subject;
  387           subjectProvided = true;
  388           loadDefaultCallbackHandler();
  389       }
  390   
  391       /**
  392        * Instantiate a new <code>LoginContext</code> object with a name
  393        * and a <code>CallbackHandler</code> object.
  394        *
  395        * <p>
  396        *
  397        * @param name the name used as the index into the
  398        *          <code>Configuration</code>. <p>
  399        *
  400        * @param callbackHandler the <code>CallbackHandler</code> object used by
  401        *          LoginModules to communicate with the user.
  402        *
  403        * @exception LoginException if the caller-specified <code>name</code>
  404        *          does not appear in the <code>Configuration</code>
  405        *          and there is no <code>Configuration</code> entry
  406        *          for "<i>other</i>", or if the caller-specified
  407        *          <code>callbackHandler</code> is <code>null</code>.
  408        *          <p>
  409        * @exception SecurityException if a SecurityManager is set and
  410        *          the caller does not have
  411        *          AuthPermission("createLoginContext.<i>name</i>"),
  412        *          or if a configuration entry for <i>name</i> does not exist and
  413        *          the caller does not additionally have
  414        *          AuthPermission("createLoginContext.other")
  415        */
  416       public LoginContext(String name, CallbackHandler callbackHandler)
  417       throws LoginException {
  418           init(name);
  419           if (callbackHandler == null)
  420               throw new LoginException(ResourcesMgr.getString
  421                                   ("invalid null CallbackHandler provided"));
  422           this.callbackHandler = new SecureCallbackHandler
  423                                   (java.security.AccessController.getContext(),
  424                                   callbackHandler);
  425       }
  426   
  427       /**
  428        * Instantiate a new <code>LoginContext</code> object with a name,
  429        * a <code>Subject</code> to be authenticated, and a
  430        * <code>CallbackHandler</code> object.
  431        *
  432        * <p>
  433        *
  434        * @param name the name used as the index into the
  435        *          <code>Configuration</code>. <p>
  436        *
  437        * @param subject the <code>Subject</code> to authenticate. <p>
  438        *
  439        * @param callbackHandler the <code>CallbackHandler</code> object used by
  440        *          LoginModules to communicate with the user.
  441        *
  442        * @exception LoginException if the caller-specified <code>name</code>
  443        *          does not appear in the <code>Configuration</code>
  444        *          and there is no <code>Configuration</code> entry
  445        *          for "<i>other</i>", or if the caller-specified
  446        *          <code>subject</code> is <code>null</code>,
  447        *          or if the caller-specified
  448        *          <code>callbackHandler</code> is <code>null</code>.
  449        *          <p>
  450        * @exception SecurityException if a SecurityManager is set and
  451        *          the caller does not have
  452        *          AuthPermission("createLoginContext.<i>name</i>"),
  453        *          or if a configuration entry for <i>name</i> does not exist and
  454        *          the caller does not additionally have
  455        *          AuthPermission("createLoginContext.other")
  456        */
  457       public LoginContext(String name, Subject subject,
  458                           CallbackHandler callbackHandler) throws LoginException {
  459           this(name, subject);
  460           if (callbackHandler == null)
  461               throw new LoginException(ResourcesMgr.getString
  462                                   ("invalid null CallbackHandler provided"));
  463           this.callbackHandler = new SecureCallbackHandler
  464                                   (java.security.AccessController.getContext(),
  465                                   callbackHandler);
  466       }
  467   
  468       /**
  469        * Instantiate a new <code>LoginContext</code> object with a name,
  470        * a <code>Subject</code> to be authenticated,
  471        * a <code>CallbackHandler</code> object, and a login
  472        * <code>Configuration</code>.
  473        *
  474        * <p>
  475        *
  476        * @param name the name used as the index into the caller-specified
  477        *          <code>Configuration</code>. <p>
  478        *
  479        * @param subject the <code>Subject</code> to authenticate,
  480        *          or <code>null</code>. <p>
  481        *
  482        * @param callbackHandler the <code>CallbackHandler</code> object used by
  483        *          LoginModules to communicate with the user, or <code>null</code>.
  484        *          <p>
  485        *
  486        * @param config the <code>Configuration</code> that lists the
  487        *          login modules to be called to perform the authentication,
  488        *          or <code>null</code>.
  489        *
  490        * @exception LoginException if the caller-specified <code>name</code>
  491        *          does not appear in the <code>Configuration</code>
  492        *          and there is no <code>Configuration</code> entry
  493        *          for "<i>other</i>".
  494        *          <p>
  495        * @exception SecurityException if a SecurityManager is set,
  496        *          <i>config</i> is <code>null</code>,
  497        *          and either the caller does not have
  498        *          AuthPermission("createLoginContext.<i>name</i>"),
  499        *          or if a configuration entry for <i>name</i> does not exist and
  500        *          the caller does not additionally have
  501        *          AuthPermission("createLoginContext.other")
  502        *
  503        * @since 1.5
  504        */
  505       public LoginContext(String name, Subject subject,
  506                           CallbackHandler callbackHandler,
  507                           Configuration config) throws LoginException {
  508           this.config = config;
  509           configProvided = (config != null) ? true : false;
  510           if (configProvided) {
  511               creatorAcc = java.security.AccessController.getContext();
  512           }
  513   
  514           init(name);
  515           if (subject != null) {
  516               this.subject = subject;
  517               subjectProvided = true;
  518           }
  519           if (callbackHandler == null) {
  520               loadDefaultCallbackHandler();
  521           } else if (!configProvided) {
  522               this.callbackHandler = new SecureCallbackHandler
  523                                   (java.security.AccessController.getContext(),
  524                                   callbackHandler);
  525           } else {
  526               this.callbackHandler = callbackHandler;
  527           }
  528       }
  529   
  530       /**
  531        * Perform the authentication.
  532        *
  533        * <p> This method invokes the <code>login</code> method for each
  534        * LoginModule configured for the <i>name</i> specified to the
  535        * <code>LoginContext</code> constructor, as determined by the login
  536        * <code>Configuration</code>.  Each <code>LoginModule</code>
  537        * then performs its respective type of authentication
  538        * (username/password, smart card pin verification, etc.).
  539        *
  540        * <p> This method completes a 2-phase authentication process by
  541        * calling each configured LoginModule's <code>commit</code> method
  542        * if the overall authentication succeeded (the relevant REQUIRED,
  543        * REQUISITE, SUFFICIENT, and OPTIONAL LoginModules succeeded),
  544        * or by calling each configured LoginModule's <code>abort</code> method
  545        * if the overall authentication failed.  If authentication succeeded,
  546        * each successful LoginModule's <code>commit</code> method associates
  547        * the relevant Principals and Credentials with the <code>Subject</code>.
  548        * If authentication failed, each LoginModule's <code>abort</code> method
  549        * removes/destroys any previously stored state.
  550        *
  551        * <p> If the <code>commit</code> phase of the authentication process
  552        * fails, then the overall authentication fails and this method
  553        * invokes the <code>abort</code> method for each configured
  554        * <code>LoginModule</code>.
  555        *
  556        * <p> If the <code>abort</code> phase
  557        * fails for any reason, then this method propagates the
  558        * original exception thrown either during the <code>login</code> phase
  559        * or the <code>commit</code> phase.  In either case, the overall
  560        * authentication fails.
  561        *
  562        * <p> In the case where multiple LoginModules fail,
  563        * this method propagates the exception raised by the first
  564        * <code>LoginModule</code> which failed.
  565        *
  566        * <p> Note that if this method enters the <code>abort</code> phase
  567        * (either the <code>login</code> or <code>commit</code> phase failed),
  568        * this method invokes all LoginModules configured for the
  569        * application regardless of their respective <code>Configuration</code>
  570        * flag parameters.  Essentially this means that <code>Requisite</code>
  571        * and <code>Sufficient</code> semantics are ignored during the
  572        * <code>abort</code> phase.  This guarantees that proper cleanup
  573        * and state restoration can take place.
  574        *
  575        * <p>
  576        *
  577        * @exception LoginException if the authentication fails.
  578        */
  579       public void login() throws LoginException {
  580   
  581           loginSucceeded = false;
  582   
  583           if (subject == null) {
  584               subject = new Subject();
  585           }
  586   
  587           try {
  588               if (configProvided) {
  589                   // module invoked in doPrivileged with creatorAcc
  590                   invokeCreatorPriv(LOGIN_METHOD);
  591                   invokeCreatorPriv(COMMIT_METHOD);
  592               } else {
  593                   // module invoked in doPrivileged
  594                   invokePriv(LOGIN_METHOD);
  595                   invokePriv(COMMIT_METHOD);
  596               }
  597               loginSucceeded = true;
  598           } catch (LoginException le) {
  599               try {
  600                   if (configProvided) {
  601                       invokeCreatorPriv(ABORT_METHOD);
  602                   } else {
  603                       invokePriv(ABORT_METHOD);
  604                   }
  605               } catch (LoginException le2) {
  606                   throw le;
  607               }
  608               throw le;
  609           }
  610       }
  611   
  612       /**
  613        * Logout the <code>Subject</code>.
  614        *
  615        * <p> This method invokes the <code>logout</code> method for each
  616        * <code>LoginModule</code> configured for this <code>LoginContext</code>.
  617        * Each <code>LoginModule</code> performs its respective logout procedure
  618        * which may include removing/destroying
  619        * <code>Principal</code> and <code>Credential</code> information
  620        * from the <code>Subject</code> and state cleanup.
  621        *
  622        * <p> Note that this method invokes all LoginModules configured for the
  623        * application regardless of their respective
  624        * <code>Configuration</code> flag parameters.  Essentially this means
  625        * that <code>Requisite</code> and <code>Sufficient</code> semantics are
  626        * ignored for this method.  This guarantees that proper cleanup
  627        * and state restoration can take place.
  628        *
  629        * <p>
  630        *
  631        * @exception LoginException if the logout fails.
  632        */
  633       public void logout() throws LoginException {
  634           if (subject == null) {
  635               throw new LoginException(ResourcesMgr.getString
  636                   ("null subject - logout called before login"));
  637           }
  638   
  639           if (configProvided) {
  640               // module invoked in doPrivileged with creatorAcc
  641               invokeCreatorPriv(LOGOUT_METHOD);
  642           } else {
  643               // module invoked in doPrivileged
  644               invokePriv(LOGOUT_METHOD);
  645           }
  646       }
  647   
  648       /**
  649        * Return the authenticated Subject.
  650        *
  651        * <p>
  652        *
  653        * @return the authenticated Subject.  If the caller specified a
  654        *          Subject to this LoginContext's constructor,
  655        *          this method returns the caller-specified Subject.
  656        *          If a Subject was not specified and authentication succeeds,
  657        *          this method returns the Subject instantiated and used for
  658        *          authentication by this LoginContext.
  659        *          If a Subject was not specified, and authentication fails or
  660        *          has not been attempted, this method returns null.
  661        */
  662       public Subject getSubject() {
  663           if (!loginSucceeded && !subjectProvided)
  664               return null;
  665           return subject;
  666       }
  667   
  668       private void clearState() {
  669           moduleIndex = 0;
  670           firstError = null;
  671           firstRequiredError = null;
  672           success = false;
  673       }
  674   
  675       private void throwException(LoginException originalError, LoginException le)
  676       throws LoginException {
  677   
  678           // first clear state
  679           clearState();
  680   
  681           // throw the exception
  682           LoginException error = (originalError != null) ? originalError : le;
  683           throw error;
  684       }
  685   
  686       /**
  687        * Invokes the login, commit, and logout methods
  688        * from a LoginModule inside a doPrivileged block.
  689        *
  690        * This version is called if the caller did not instantiate
  691        * the LoginContext with a Configuration object.
  692        */
  693       private void invokePriv(final String methodName) throws LoginException {
  694           try {
  695               java.security.AccessController.doPrivileged
  696                   (new java.security.PrivilegedExceptionAction<Void>() {
  697                   public Void run() throws LoginException {
  698                       invoke(methodName);
  699                       return null;
  700                   }
  701               });
  702           } catch (java.security.PrivilegedActionException pae) {
  703               throw (LoginException)pae.getException();
  704           }
  705       }
  706   
  707       /**
  708        * Invokes the login, commit, and logout methods
  709        * from a LoginModule inside a doPrivileged block restricted
  710        * by creatorAcc
  711        *
  712        * This version is called if the caller instantiated
  713        * the LoginContext with a Configuration object.
  714        */
  715       private void invokeCreatorPriv(final String methodName)
  716                   throws LoginException {
  717           try {
  718               java.security.AccessController.doPrivileged
  719                   (new java.security.PrivilegedExceptionAction<Void>() {
  720                   public Void run() throws LoginException {
  721                       invoke(methodName);
  722                       return null;
  723                   }
  724               }, creatorAcc);
  725           } catch (java.security.PrivilegedActionException pae) {
  726               throw (LoginException)pae.getException();
  727           }
  728       }
  729   
  730       private void invoke(String methodName) throws LoginException {
  731   
  732           // start at moduleIndex
  733           // - this can only be non-zero if methodName is LOGIN_METHOD
  734   
  735           for (int i = moduleIndex; i < moduleStack.length; i++, moduleIndex++) {
  736               try {
  737   
  738                   int mIndex = 0;
  739                   Method[] methods = null;
  740   
  741                   if (moduleStack[i].module != null) {
  742                       methods = moduleStack[i].module.getClass().getMethods();
  743                   } else {
  744   
  745                       // instantiate the LoginModule
  746                       Class c = Class.forName
  747                                   (moduleStack[i].entry.getLoginModuleName(),
  748                                   true,
  749                                   contextClassLoader);
  750   
  751                       Constructor constructor = c.getConstructor(PARAMS);
  752                       Object[] args = { };
  753   
  754                       // allow any object to be a LoginModule
  755                       // as long as it conforms to the interface
  756                       moduleStack[i].module = constructor.newInstance(args);
  757   
  758                       methods = moduleStack[i].module.getClass().getMethods();
  759   
  760                       // call the LoginModule's initialize method
  761                       for (mIndex = 0; mIndex < methods.length; mIndex++) {
  762                           if (methods[mIndex].getName().equals(INIT_METHOD))
  763                               break;
  764                       }
  765   
  766                       Object[] initArgs = {subject,
  767                                           callbackHandler,
  768                                           state,
  769                                           moduleStack[i].entry.getOptions() };
  770                       // invoke the LoginModule initialize method
  771                       methods[mIndex].invoke(moduleStack[i].module, initArgs);
  772                   }
  773   
  774                   // find the requested method in the LoginModule
  775                   for (mIndex = 0; mIndex < methods.length; mIndex++) {
  776                       if (methods[mIndex].getName().equals(methodName))
  777                           break;
  778                   }
  779   
  780                   // set up the arguments to be passed to the LoginModule method
  781                   Object[] args = { };
  782   
  783                   // invoke the LoginModule method
  784                   boolean status = ((Boolean)methods[mIndex].invoke
  785                                   (moduleStack[i].module, args)).booleanValue();
  786   
  787                   if (status == true) {
  788   
  789                       // if SUFFICIENT, return if no prior REQUIRED errors
  790                       if (!methodName.equals(ABORT_METHOD) &&
  791                           !methodName.equals(LOGOUT_METHOD) &&
  792                           moduleStack[i].entry.getControlFlag() ==
  793                       AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT &&
  794                           firstRequiredError == null) {
  795   
  796                           // clear state
  797                           clearState();
  798   
  799                           if (debug != null)
  800                               debug.println(methodName + " SUFFICIENT success");
  801                           return;
  802                       }
  803   
  804                       if (debug != null)
  805                           debug.println(methodName + " success");
  806                       success = true;
  807                   } else {
  808                       if (debug != null)
  809                           debug.println(methodName + " ignored");
  810                   }
  811   
  812               } catch (NoSuchMethodException nsme) {
  813                   MessageFormat form = new MessageFormat(ResourcesMgr.getString
  814                           ("unable to instantiate LoginModule, module, because " +
  815                           "it does not provide a no-argument constructor"));
  816                   Object[] source = {moduleStack[i].entry.getLoginModuleName()};
  817                   throwException(null, new LoginException(form.format(source)));
  818               } catch (InstantiationException ie) {
  819                   throwException(null, new LoginException(ResourcesMgr.getString
  820                           ("unable to instantiate LoginModule: ") +
  821                           ie.getMessage()));
  822               } catch (ClassNotFoundException cnfe) {
  823                   throwException(null, new LoginException(ResourcesMgr.getString
  824                           ("unable to find LoginModule class: ") +
  825                           cnfe.getMessage()));
  826               } catch (IllegalAccessException iae) {
  827                   throwException(null, new LoginException(ResourcesMgr.getString
  828                           ("unable to access LoginModule: ") +
  829                           iae.getMessage()));
  830               } catch (InvocationTargetException ite) {
  831   
  832                   // failure cases
  833   
  834                   LoginException le;
  835   
  836                   if (ite.getCause() instanceof PendingException &&
  837                       methodName.equals(LOGIN_METHOD)) {
  838   
  839                       // XXX
  840                       //
  841                       // if a module's LOGIN_METHOD threw a PendingException
  842                       // then immediately throw it.
  843                       //
  844                       // when LoginContext is called again,
  845                       // the module that threw the exception is invoked first
  846                       // (the module list is not invoked from the start).
  847                       // previously thrown exception state is still present.
  848                       //
  849                       // it is assumed that the module which threw
  850                       // the exception can have its
  851                       // LOGIN_METHOD invoked twice in a row
  852                       // without any commit/abort in between.
  853                       //
  854                       // in all cases when LoginContext returns
  855                       // (either via natural return or by throwing an exception)
  856                       // we need to call clearState before returning.
  857                       // the only time that is not true is in this case -
  858                       // do not call throwException here.
  859   
  860                       throw (PendingException)ite.getCause();
  861   
  862                   } else if (ite.getCause() instanceof LoginException) {
  863   
  864                       le = (LoginException)ite.getCause();
  865   
  866                   } else if (ite.getCause() instanceof SecurityException) {
  867   
  868                       // do not want privacy leak
  869                       // (e.g., sensitive file path in exception msg)
  870   
  871                       le = new LoginException("Security Exception");
  872                       le.initCause(new SecurityException());
  873                       if (debug != null) {
  874                           debug.println
  875                               ("original security exception with detail msg " +
  876                               "replaced by new exception with empty detail msg");
  877                           debug.println("original security exception: " +
  878                                   ite.getCause().toString());
  879                       }
  880                   } else {
  881   
  882                       // capture an unexpected LoginModule exception
  883                       java.io.StringWriter sw = new java.io.StringWriter();
  884                       ite.getCause().printStackTrace
  885                                                   (new java.io.PrintWriter(sw));
  886                       sw.flush();
  887                       le = new LoginException(sw.toString());
  888                   }
  889   
  890                   if (moduleStack[i].entry.getControlFlag() ==
  891                       AppConfigurationEntry.LoginModuleControlFlag.REQUISITE) {
  892   
  893                       if (debug != null)
  894                           debug.println(methodName + " REQUISITE failure");
  895   
  896                       // if REQUISITE, then immediately throw an exception
  897                       if (methodName.equals(ABORT_METHOD) ||
  898                           methodName.equals(LOGOUT_METHOD)) {
  899                           if (firstRequiredError == null)
  900                               firstRequiredError = le;
  901                       } else {
  902                           throwException(firstRequiredError, le);
  903                       }
  904   
  905                   } else if (moduleStack[i].entry.getControlFlag() ==
  906                       AppConfigurationEntry.LoginModuleControlFlag.REQUIRED) {
  907   
  908                       if (debug != null)
  909                           debug.println(methodName + " REQUIRED failure");
  910   
  911                       // mark down that a REQUIRED module failed
  912                       if (firstRequiredError == null)
  913                           firstRequiredError = le;
  914   
  915                   } else {
  916   
  917                       if (debug != null)
  918                           debug.println(methodName + " OPTIONAL failure");
  919   
  920                       // mark down that an OPTIONAL module failed
  921                       if (firstError == null)
  922                           firstError = le;
  923                   }
  924               }
  925           }
  926   
  927           // we went thru all the LoginModules.
  928           if (firstRequiredError != null) {
  929               // a REQUIRED module failed -- return the error
  930               throwException(firstRequiredError, null);
  931           } else if (success == false && firstError != null) {
  932               // no module succeeded -- return the first error
  933               throwException(firstError, null);
  934           } else if (success == false) {
  935               // no module succeeded -- all modules were IGNORED
  936               throwException(new LoginException
  937                   (ResourcesMgr.getString("Login Failure: all modules ignored")),
  938                   null);
  939           } else {
  940               // success
  941   
  942               clearState();
  943               return;
  944           }
  945       }
  946   
  947       /**
  948        * Wrap the caller-specified CallbackHandler in our own
  949        * and invoke it within a privileged block, constrained by
  950        * the caller's AccessControlContext.
  951        */
  952       private static class SecureCallbackHandler implements CallbackHandler {
  953   
  954           private final java.security.AccessControlContext acc;
  955           private final CallbackHandler ch;
  956   
  957           SecureCallbackHandler(java.security.AccessControlContext acc,
  958                           CallbackHandler ch) {
  959               this.acc = acc;
  960               this.ch = ch;
  961           }
  962   
  963           public void handle(final Callback[] callbacks)
  964                   throws java.io.IOException, UnsupportedCallbackException {
  965               try {
  966                   java.security.AccessController.doPrivileged
  967                       (new java.security.PrivilegedExceptionAction<Void>() {
  968                       public Void run() throws java.io.IOException,
  969                                           UnsupportedCallbackException {
  970                           ch.handle(callbacks);
  971                           return null;
  972                       }
  973                   }, acc);
  974               } catch (java.security.PrivilegedActionException pae) {
  975                   if (pae.getException() instanceof java.io.IOException) {
  976                       throw (java.io.IOException)pae.getException();
  977                   } else {
  978                       throw (UnsupportedCallbackException)pae.getException();
  979                   }
  980               }
  981           }
  982       }
  983   
  984       /**
  985        * LoginModule information -
  986        *          incapsulates Configuration info and actual module instances
  987        */
  988       private static class ModuleInfo {
  989           AppConfigurationEntry entry;
  990           Object module;
  991   
  992           ModuleInfo(AppConfigurationEntry newEntry, Object newModule) {
  993               this.entry = newEntry;
  994               this.module = newModule;
  995           }
  996       }
  997   }

Save This Page
Home » openjdk-7 » javax.security » auth » login » [javadoc | source]