1 28 package net.sf.jguard.ext.authentication.loginmodules; 29 30 import java.text.MessageFormat ; 31 import java.util.ArrayList ; 32 import java.util.HashMap ; 33 import java.util.HashSet ; 34 import java.util.Hashtable ; 35 import java.util.Iterator ; 36 import java.util.List ; 37 import java.util.Map ; 38 import java.util.Set ; 39 import java.util.logging.Level ; 40 import java.util.logging.Logger ; 41 42 import javax.naming.CompositeName ; 43 import javax.naming.Context ; 44 import javax.naming.InitialContext ; 45 import javax.naming.Name ; 46 import javax.naming.NameParser ; 47 import javax.naming.NamingEnumeration ; 48 import javax.naming.NamingException ; 49 import javax.naming.directory.Attribute ; 50 import javax.naming.directory.Attributes ; 51 import javax.naming.directory.DirContext ; 52 import javax.naming.directory.SearchControls ; 53 import javax.naming.directory.SearchResult ; 54 import javax.naming.ldap.Control ; 55 import javax.naming.ldap.InitialLdapContext ; 56 import javax.security.auth.Subject ; 57 import javax.security.auth.callback.CallbackHandler ; 58 import javax.security.auth.login.FailedLoginException ; 59 import javax.security.auth.login.LoginException ; 60 import javax.security.auth.spi.LoginModule ; 61 62 import net.sf.jguard.core.authentication.credentials.JGuardCredential; 63 import net.sf.jguard.ext.SecurityConstants; 64 import net.sf.jguard.ext.util.FastBindConnectionControl; 65 import net.sf.jguard.ext.util.JNDIUtils; 66 67 72 public class JNDILoginModule extends UserLoginModule implements LoginModule { 73 74 private static final String USER_DN = "userDN"; 75 private static final String CONTEXTFORCOMMIT = "contextforcommit"; 76 private static final String JNDI = "jndi"; 77 private static final String TIMELIMIT = "timelimit"; 78 private static final String SEARCHSCOPE = "searchscope"; 79 private static final String RETURNINGOBJFLAG = "returningobjflag"; 80 private static final String RETURNINGATTRIBUTES = "returningattributes"; 81 private static final String DEREFLINKFLAG = "dereflinkflag"; 82 private static final String COUNTLIMIT = "countlimit"; 83 private static final String SEARCHCONTROLS = "searchcontrols."; 84 private static final String PREAUTH = "preauth."; 86 private static final String AUTH = "auth."; 87 private static final String FAST_BIND_CONNECTION = "fastBindConnection"; 88 private static final String SEARCH_FILTER = "search.filter"; 89 private static final String SEARCH_BASE_DN = "search.base.dn"; 90 91 92 private static final Logger logger = Logger.getLogger(JNDILoginModule.class.getName()); 93 94 95 private DirContext preAuthContext = null; 96 private DirContext authContext = null; 97 98 private SearchControls preAuthSearchControls = null; 100 101 private Map authOpts = null; 102 private Map preAuthOpts = null; 103 private Map preAuthSearchControlsOpts = null; 104 105 private Set credentials = null; 106 107 114 public void initialize(Subject subj, CallbackHandler cbkHandler, Map sState, Map opts) { 115 super.initialize(subj, cbkHandler, sState, opts); 116 preAuthOpts = new HashMap (); 117 preAuthSearchControlsOpts = new HashMap (); 118 authOpts = new HashMap (); 119 120 fillOptions(); 122 123 } 124 125 private DirContext getContext(Map opts) throws LoginException { 126 DirContext context = null; 127 if(opts.containsKey(JNDILoginModule.JNDI)){ 128 try { 129 Context initDirContext = new InitialContext (); 130 context = (DirContext ) initDirContext.lookup((String )opts.get(JNDILoginModule.JNDI)); 131 } catch (NamingException e) { 132 throw new LoginException (" we cannot grab the default initial context "); 133 } 134 135 }else{ 136 Control [] LDAPcontrols = getLDAPControls(opts); 137 try { 138 context = new InitialLdapContext (new Hashtable (opts),LDAPcontrols); 139 } catch (NamingException e) { 140 throw new LoginException (e.getMessage()); 141 } 142 } 143 if(context == null){ 144 throw new LoginException (" we cannot grab the default initial context "); 145 } 146 return context; 147 148 } 149 150 158 private void fillOptions() { 159 Iterator entriesIterator = options.entrySet().iterator(); 160 while(entriesIterator.hasNext()){ 161 Map.Entry entry = (Map.Entry )entriesIterator.next(); 162 String key = (String )entry.getKey(); 163 String value = (String )entry.getValue(); 164 if(key.startsWith(JNDILoginModule.PREAUTH)){ 165 key = key.substring(8, key.length()); 166 if(key.startsWith(JNDILoginModule.SEARCHCONTROLS)){ 167 key = key.substring(15, key.length()); 168 preAuthSearchControlsOpts.put(key, value); 169 }else{ 170 preAuthOpts.put(key, value); 171 } 172 }else if(key.startsWith(JNDILoginModule.AUTH)){ 173 key = key.substring(5, key.length()); 174 authOpts.put(key, value); 175 } 176 } 177 } 178 179 184 public boolean login() throws LoginException { 185 super.login(); 186 if(SecurityConstants.GUEST.equals(login)){ 187 loginOK = false; 189 return false; 190 } 191 192 193 194 String userDN = (String )authOpts.get(USER_DN); 198 if(preAuthOpts.size()==0 &&(userDN==null || userDN.equals(""))){ 199 throw new IllegalArgumentException (" you've configured the JNDILoginmodule in 'auth' mode (options starting by 'preauth.' are not present).\n 'auth.userDN' option used to find the user LDAP Entry is lacking or is empty "); 200 } 201 202 203 userDN = getuserDN(userDN, login); 204 205 if (userDN != null && !equals("")) { 206 authOpts.put(Context.SECURITY_PRINCIPAL, userDN); 207 authOpts.put(Context.SECURITY_CREDENTIALS, new String (password)); 208 try { 209 authContext = getContext(authOpts); 210 } finally { 211 try { 212 if(authContext!= null){ 213 authContext.close(); 214 } 215 } catch (NamingException e) { 216 throw new FailedLoginException (e.getMessage()); 217 } 218 } 219 }else{ 221 loginOK = false; 222 throw new LoginException (" Distinguished name is null or empty "); 223 } 224 sharedState.put(SecurityConstants.SKIP_PASSWORD_CHECK, "true"); 227 logger.log(Level.INFO, " JNDI login phase succeed for user "+login); 228 return true; 229 } 230 231 240 private String getuserDN(String userDN, String login) throws LoginException { 241 String escapedLogin = JNDIUtils.escapeDn(login); 243 Object [] args = {escapedLogin}; 244 if(preAuthOpts.size()>0){ 245 246 try { 248 preAuthContext = getContext(preAuthOpts); 249 } catch (LoginException e) { 250 loginOK = false; 251 throw new IllegalArgumentException (e.getMessage()); 252 } 253 preAuthSearchControlsOpts.put(COUNTLIMIT, "1"); 254 preAuthSearchControls = getSearchControls(preAuthSearchControlsOpts); 255 256 try{ 257 userDN = preAuthSearch(preAuthContext, preAuthSearchControls); 258 }catch(LoginException e){ 259 loginOK = false; 260 throw e; 261 }finally{ 262 try { 263 preAuthContext.close(); 264 } catch (NamingException e) { 265 logger.severe(e.getMessage()); 266 } 267 } 268 }else{ 269 userDN = MessageFormat.format(userDN, args); 270 userDN = JNDIUtils.escapeDn(userDN); 271 } 272 return userDN; 273 } 274 275 276 277 281 public boolean commit() throws LoginException { 282 if(!loginOK){ 283 return false; 284 } 285 if(options.containsKey(JNDILoginModule.CONTEXTFORCOMMIT)&&options.get(JNDILoginModule.CONTEXTFORCOMMIT).equals("true")){ 286 credentials = grabAttributes(getContext(authOpts),(String )authOpts.get(USER_DN)); 287 } 288 289 if(credentials!=null){ 290 Set privateCredentials = subject.getPrivateCredentials(); 291 privateCredentials.addAll(credentials); 292 } 293 return true; 294 } 295 296 304 private Set grabAttributes(DirContext contextUsedForCommit,String userDN) throws LoginException { 305 DirContext userCtxt = null; 306 Set creds = new HashSet (); 307 try { 308 userCtxt = (DirContext ) contextUsedForCommit.lookup(getuserDN(userDN,login)); 309 if(userCtxt==null){ 310 throw new FailedLoginException ("login.user.does.not.exist"); 311 } 312 313 Attributes attributes = userCtxt.getAttributes(""); 314 creds = grabCredentials(attributes); 315 } catch (NamingException e) { 316 throw new LoginException (e.getMessage()); 317 }finally{ 318 try { 319 if(userCtxt!=null){ 320 userCtxt.close(); 321 } 322 } catch (NamingException e) { 323 throw new LoginException (e.getMessage()); 324 } 325 } 326 327 return creds; 328 } 329 330 337 private Set grabCredentials(Attributes atts) throws NamingException { 338 Set credentials = new HashSet (); 339 NamingEnumeration enumeration = atts.getAll(); 340 341 while(enumeration.hasMore()){ 342 Attribute attribute = (Attribute )enumeration.next(); 343 String key = attribute.getID(); 344 String value= JNDIUtils.getAttributeValue(attribute); 345 JGuardCredential credential = new JGuardCredential(); 346 credential.setId(key); 347 credential.setValue(value); 348 credentials.add(credential); 349 } 350 351 return credentials; 352 } 353 354 359 private String preAuthSearch(DirContext context,SearchControls controls) throws LoginException { 360 NamingEnumeration results = null; 361 String dn = null; 362 String baseDN = null; 363 String searchFilter = null; 364 try { 365 String [] filterArgs = new String []{super.login}; 366 Hashtable opts = context.getEnvironment(); 367 baseDN = (String )opts.get(JNDILoginModule.SEARCH_BASE_DN); 368 searchFilter = (String )opts.get(JNDILoginModule.SEARCH_FILTER); 369 370 results = context.search(baseDN,searchFilter,filterArgs, controls); 371 int userFound = 0; 372 boolean grabInformations = false; 373 String contextforcommit = (String )options.get(JNDILoginModule.CONTEXTFORCOMMIT); 374 if (contextforcommit!=null && "preauth".equals(contextforcommit)){ 375 grabInformations = true; 376 } 377 while(results.hasMore()){ 378 SearchResult result = (SearchResult ) results.next(); 379 dn = result.getName(); 381 NameParser pn = context.getNameParser(""); 383 CompositeName compName = new CompositeName (result.getName()); 385 386 Name entryName = pn.parse(compName.get(0)); 388 dn = entryName.toString(); 393 394 if(grabInformations){ 395 credentials = grabCredentials(result.getAttributes()); 396 } 397 398 userFound++; 399 } 400 if(userFound>1){ 401 logger.warning("more than one Distinguished Name has been found in the Directory for the user="+login); 402 throw new FailedLoginException ("login.error"); 403 } 404 } catch (NamingException e) { 405 throw new LoginException (" a naming exception has been raised when we are looking for the user Distinguished Name "+e.getMessage()); 406 }finally{ 407 try { 408 context.close(); 409 } catch (NamingException e) { 410 throw new LoginException (e.getMessage()); 411 } 412 } 413 if(dn==null){ 414 throw new FailedLoginException ("login.error"); 415 } 416 return dn; 417 } 418 419 private SearchControls getSearchControls(Map opts){ 420 SearchControls controls = new SearchControls (); 421 Iterator itEntries = opts.entrySet().iterator(); 422 while(itEntries.hasNext()){ 423 Map.Entry entry = (Map.Entry )itEntries.next(); 424 String key = (String )entry.getKey(); 425 String value = (String )entry.getValue(); 426 if(JNDILoginModule.COUNTLIMIT.equals(key)){ 427 long countLimit = Long.parseLong(value); 428 controls.setCountLimit(countLimit); 429 }else if(JNDILoginModule.DEREFLINKFLAG.equals(key)){ 430 boolean derefLinkFlag = Boolean.valueOf(value).booleanValue(); 431 controls.setDerefLinkFlag(derefLinkFlag); 432 }else if(JNDILoginModule.RETURNINGATTRIBUTES.equals(key)){ 433 String [] returningAttributes = value.split("#"); 434 controls.setReturningAttributes(returningAttributes); 435 }else if(JNDILoginModule.RETURNINGOBJFLAG.equals(key)){ 436 boolean returningobjflag = Boolean.valueOf(value).booleanValue(); 437 controls.setReturningObjFlag(returningobjflag); 438 }else if(JNDILoginModule.SEARCHSCOPE.equals(key)){ 439 int scope = Integer.parseInt(value); 440 controls.setSearchScope(scope); 441 }else if(JNDILoginModule.TIMELIMIT.equals(key)){ 442 int timelimit = Integer.parseInt(value); 443 controls.setTimeLimit(timelimit); 444 } 445 } 446 447 return controls; 448 } 449 450 451 private Control [] getLDAPControls(Map opts){ 452 List ldapControls = new ArrayList (); 453 if(opts.containsKey(JNDILoginModule.FAST_BIND_CONNECTION) 454 && "true".equalsIgnoreCase((String )opts.get(JNDILoginModule.FAST_BIND_CONNECTION))){ 455 ldapControls.add(new FastBindConnectionControl()); 456 } 457 return (Control []) ldapControls.toArray(new Control [ldapControls.size()]); 458 459 } 460 461 } 462 | Popular Tags |