View Javadoc

1   /***
2    *  Copyright 2003-2007 Greg Luck
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  
17  package net.sf.jpam.jaas;
18  
19  import net.sf.jpam.Pam;
20  import net.sf.jpam.PamReturnValue;
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  
24  import javax.security.auth.Subject;
25  import javax.security.auth.callback.Callback;
26  import javax.security.auth.callback.CallbackHandler;
27  import javax.security.auth.callback.NameCallback;
28  import javax.security.auth.callback.PasswordCallback;
29  import javax.security.auth.callback.UnsupportedCallbackException;
30  import javax.security.auth.login.AccountExpiredException;
31  import javax.security.auth.login.CredentialExpiredException;
32  import javax.security.auth.login.FailedLoginException;
33  import javax.security.auth.login.LoginException;
34  import javax.security.auth.spi.LoginModule;
35  import java.io.IOException;
36  import java.util.Map;
37  
38  /***
39   * A <code>LoginModule</code> which invokes JPAM. This can be used from standard
40   * JAAS implementations. It is an alternative to directly using the {@link Pam} class.
41   * <p/>
42   * This class relies on the existence of a .java.login.config with a login configuration
43   * called <code>net-sf-jpam</code>. Copy the .java.login.config in the src/config/<architecture>
44   * directory to running user's home directory.
45   * @author <a href="mailto:gregluck@users.sourceforge.net">Greg Luck</a>
46   * @version $Id: JpamLoginModule.java 19 2007-04-01 23:13:48Z gregluck $
47   */
48  public class JpamLoginModule implements LoginModule {
49      private static final Log LOG = LogFactory.getLog(JpamLoginModule.class.getName());
50      private static final String SERVICE_NAME_OPTION = "serviceName";
51      private Subject subject;
52      private CallbackHandler callbackHandler;
53      private Map sharedState;
54      private Map options;
55      private Pam pam;
56  
57      /***
58       * Method to abort the authentication process (phase 2).
59       * <p/>
60       * <p> This method is called if the LoginContext's
61       * overall authentication failed.
62       * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
63       * did not succeed).
64       * <p/>
65       * <p> If this LoginModule's own authentication attempt
66       * succeeded (checked by retrieving the private state saved by the
67       * <code>login</code> method), then this method cleans up any state
68       * that was originally saved.
69       * <p/>
70       * <p/>
71       *
72       * @return true if this method succeeded, or false if this
73       *         <code>LoginModule</code> should be ignored.
74       * @throws javax.security.auth.login.LoginException
75       *          if the abort fails
76       */
77      public boolean abort() throws LoginException {
78          return true;
79      }
80  
81      /***
82       * Method to commit the authentication process (phase 2).
83       * <p/>
84       * <p> This method is called if the LoginContext's
85       * overall authentication succeeded
86       * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
87       * succeeded).
88       * <p/>
89       * <p> If this LoginModule's own authentication attempt
90       * succeeded (checked by retrieving the private state saved by the
91       * <code>login</code> method), then this method associates relevant
92       * Principals and Credentials with the <code>Subject</code> located in the
93       * <code>LoginModule</code>.  If this LoginModule's own
94       * authentication attempted failed, then this method removes/destroys
95       * any state that was originally saved.
96       * <p/>
97       * <p/>
98       *
99       * @return true if this method succeeded, or false if this
100      *         <code>LoginModule</code> should be ignored.
101      * @throws javax.security.auth.login.LoginException
102      *          if the commit fails
103      */
104     public boolean commit() throws LoginException {
105         return true;
106     }
107 
108     /***
109      * Method to authenticate a <code>Subject</code> (phase 1).
110      * <p/>
111      * <p> The implementation of this method authenticates
112      * a <code>Subject</code>.  For example, it may prompt for
113      * <code>Subject</code> information such
114      * as a username and password and then attempt to verify the password.
115      * This method saves the result of the authentication attempt
116      * as private state within the LoginModule.
117      * <p/>
118      * <p/>
119      *
120      * @return true if the authentication succeeded, or false if this
121      *         <code>LoginModule</code> should be ignored.
122      * @throws javax.security.auth.login.LoginException
123      *          if the authentication fails
124      */
125     public boolean login() throws LoginException {
126         pam = createPam();
127 
128         Callback[] callbacks = new Callback[2];
129         String username = null;
130         NameCallback nameCallback = new NameCallback("Enter Username: ");
131         callbacks[0] = nameCallback;
132         String credentials = null;
133         PasswordCallback passwordCallback = new PasswordCallback("Enter Credentials: ", false);
134         callbacks[1] = passwordCallback;
135 
136         try {
137             callbackHandler.handle(callbacks);
138         } catch (IOException e) {
139             LOG.error("IOException handling login: " + e.getMessage(), e);
140             throw new LoginException(e.getMessage());
141         } catch (UnsupportedCallbackException e) {
142             LOG.error("UnsupportedCallbackException handling login: " + e.getMessage(), e);
143             throw new LoginException(e.getMessage());
144         }
145         username = nameCallback.getName();
146         credentials = String.copyValueOf(passwordCallback.getPassword());
147         boolean authenticated = false;
148         PamReturnValue pamReturnValue = pam.authenticate(username, credentials);
149         if (pamReturnValue.equals(PamReturnValue.PAM_SUCCESS)) {
150             authenticated = true;
151         } else if (pamReturnValue.equals(PamReturnValue.PAM_ACCT_EXPIRED)) {
152             throw new AccountExpiredException(PamReturnValue.PAM_ACCT_EXPIRED.toString());
153         } else if (pamReturnValue.equals(PamReturnValue.PAM_CRED_EXPIRED)) {
154             throw new CredentialExpiredException(PamReturnValue.PAM_CRED_EXPIRED.toString());
155         } else {
156             throw new FailedLoginException(pamReturnValue.toString());
157         }
158         return authenticated;
159     }
160 
161     private Pam createPam() {
162         String serviceName = (String)options.get(SERVICE_NAME_OPTION);
163         if (serviceName == null) {
164             LOG.debug("No serviceName configured in JAAS configuration file. Using default service name of "
165             + Pam.DEFAULT_SERVICE_NAME);
166             serviceName = Pam.DEFAULT_SERVICE_NAME;
167         } else {
168             LOG.debug("Using service name of "
169             + serviceName + " from JAAS configuration file");
170         }
171         Pam pam = new Pam(serviceName);
172         return pam;
173     }
174 
175     /***
176      * Method which logs out a <code>Subject</code>.
177      * <p/>
178      * <p>An implementation of this method might remove/destroy a Subject's
179      * Principals and Credentials.
180      * <p/>
181      * <p/>
182      *
183      * @return true if this method succeeded, or false if this
184      *         <code>LoginModule</code> should be ignored.
185      * @throws javax.security.auth.login.LoginException
186      *          if the logout fails
187      */
188     public boolean logout() throws LoginException {
189         return true;
190     }
191 
192     /***
193      * Initialize this LoginModule.
194      * <p/>
195      * <p> This method is called by the <code>LoginContext</code>
196      * after this <code>LoginModule</code> has been instantiated.
197      * The purpose of this method is to initialize this
198      * <code>LoginModule</code> with the relevant information.
199      * If this <code>LoginModule</code> does not understand
200      * any of the data stored in <code>sharedState</code> or
201      * <code>options</code> parameters, they can be ignored.
202      * <p/>
203      * <p/>
204      *
205      * @param subject         the <code>Subject</code> to be authenticated. <p>
206      * @param callbackHandler a <code>CallbackHandler</code> for communicating
207      *                        with the end user (prompting for usernames and
208      *                        passwords, for example). <p>
209      * @param sharedState     state shared with other configured LoginModules. <p>
210      * @param options         options specified in the login
211      *                        <code>Configuration</code> for this particular
212      *                        <code>LoginModule</code>.
213      */
214     public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
215         this.subject = subject;
216         this.callbackHandler = callbackHandler;
217         this.sharedState = sharedState;
218         this.options = options;
219     }
220 
221 
222     /***
223      * Get the underlying PAM object
224      */
225     public Pam getPam() {
226         return pam;
227     }
228 }