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 }