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 }