1 17 package org.apache.geronimo.security.jaas; 18 19 import java.security.InvalidKeyException ; 20 import java.security.NoSuchAlgorithmException ; 21 import java.security.Principal ; 22 import java.util.ArrayList ; 23 import java.util.Collection ; 24 import java.util.HashMap ; 25 import java.util.Hashtable ; 26 import java.util.Iterator ; 27 import java.util.LinkedList ; 28 import java.util.List ; 29 import java.util.Map ; 30 import javax.crypto.Mac; 31 import javax.crypto.SecretKey; 32 import javax.crypto.spec.SecretKeySpec; 33 import javax.management.ObjectName ; 34 import javax.security.auth.Subject ; 35 import javax.security.auth.callback.Callback ; 36 import javax.security.auth.login.LoginException ; 37 import javax.security.auth.spi.LoginModule ; 38 39 import EDU.oswego.cs.dl.util.concurrent.ClockDaemon; 40 import EDU.oswego.cs.dl.util.concurrent.ThreadFactory; 41 42 import org.apache.commons.logging.Log; 43 import org.apache.commons.logging.LogFactory; 44 import org.apache.geronimo.common.GeronimoSecurityException; 45 import org.apache.geronimo.gbean.GBeanInfo; 46 import org.apache.geronimo.gbean.GBeanInfoBuilder; 47 import org.apache.geronimo.gbean.GBeanLifecycle; 48 import org.apache.geronimo.gbean.ReferenceCollection; 49 import org.apache.geronimo.kernel.jmx.JMXUtil; 50 import org.apache.geronimo.security.ContextManager; 51 import org.apache.geronimo.security.IdentificationPrincipal; 52 import org.apache.geronimo.security.SubjectId; 53 import org.apache.geronimo.security.realm.SecurityRealm; 54 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory; 55 56 64 public class JaasLoginService implements GBeanLifecycle, JaasLoginServiceMBean { 65 public static final ObjectName OBJECT_NAME = JMXUtil.getObjectName("geronimo.server:J2EEApplication=null,J2EEModule=org/apache/geronimo/Server,J2EEServer=geronimo,j2eeType=GBean,name=JaasLoginService"); 66 public static final Log log = LogFactory.getLog(JaasLoginService.class); 67 private final static int DEFAULT_EXPIRED_LOGIN_SCAN_INTERVAL = 300000; private final static int DEFAULT_MAX_LOGIN_DURATION = 1000 * 3600 * 24; private final static ClockDaemon clockDaemon; 70 private static long nextLoginModuleId = System.currentTimeMillis(); 71 private ReferenceCollection realms; 72 private Object expiredLoginScanIdentifier; 73 private final SecretKey key; 74 private final String algorithm; 75 private final ClassLoader classLoader; 76 private final Map activeLogins = new Hashtable (); 77 private int expiredLoginScanIntervalMillis = DEFAULT_EXPIRED_LOGIN_SCAN_INTERVAL; 78 private int maxLoginDurationMillis = DEFAULT_MAX_LOGIN_DURATION; 79 80 public JaasLoginService(String algorithm, String password, ClassLoader classLoader) { 81 this.classLoader = classLoader; 82 this.algorithm = algorithm; 83 key = new SecretKeySpec(password.getBytes(), algorithm); 85 } 86 87 90 public Collection getRealms() throws GeronimoSecurityException { 91 return realms; 92 } 93 94 97 public void setRealms(Collection realms) { 98 this.realms = (ReferenceCollection) realms; 99 } 101 102 105 public int getMaxLoginDurationMillis() { 106 return maxLoginDurationMillis; 107 } 108 109 112 public void setMaxLoginDurationMillis(int maxLoginDurationMillis) { 113 if(maxLoginDurationMillis == 0) { 114 maxLoginDurationMillis = DEFAULT_MAX_LOGIN_DURATION; 115 } 116 this.maxLoginDurationMillis = maxLoginDurationMillis; 117 } 118 119 122 public int getExpiredLoginScanIntervalMillis() { 123 return expiredLoginScanIntervalMillis; 124 } 125 126 129 public void setExpiredLoginScanIntervalMillis(int expiredLoginScanIntervalMillis) { 130 if(expiredLoginScanIntervalMillis == 0) { 131 expiredLoginScanIntervalMillis = DEFAULT_EXPIRED_LOGIN_SCAN_INTERVAL; 132 } 133 this.expiredLoginScanIntervalMillis = expiredLoginScanIntervalMillis; 134 } 135 136 public void doStart() throws Exception { 137 expiredLoginScanIdentifier = clockDaemon.executePeriodically(expiredLoginScanIntervalMillis, new ExpirationMonitor(), true); 138 } 139 140 public void doStop() throws Exception { 141 ClockDaemon.cancel(expiredLoginScanIdentifier); 142 } 144 145 public void doFail() { 146 } 148 149 158 public JaasClientId connectToRealm(String realmName) { 159 SecurityRealm realm = null; 160 realm = getRealm(realmName); 161 if(realm == null) { 162 throw new GeronimoSecurityException("No such realm ("+realmName+")"); 163 } else { 164 return initializeClient(realm); 165 } 166 } 167 168 172 public JaasLoginModuleConfiguration[] getLoginConfiguration(JaasClientId userIdentifier) throws LoginException { 173 JaasSecurityContext context = (JaasSecurityContext) activeLogins.get(userIdentifier); 174 if(context == null) { 175 throw new ExpiredLoginModuleException(); 176 } 177 JaasLoginModuleConfiguration[] config = context.getModules(); 178 JaasLoginModuleConfiguration[] result = new JaasLoginModuleConfiguration[config.length]; 180 for (int i = 0; i < config.length; i++) { 181 result[i] = config[i].getSerializableCopy(); 182 } 183 return result; 184 } 185 186 193 public Callback [] getServerLoginCallbacks(JaasClientId userIdentifier, int loginModuleIndex) throws LoginException { 194 JaasSecurityContext context = (JaasSecurityContext) activeLogins.get(userIdentifier); 195 if(context == null) { 196 throw new ExpiredLoginModuleException(); 197 } 198 if(loginModuleIndex < 0 || loginModuleIndex >= context.getModules().length || !context.getModules()[loginModuleIndex].isServerSide()) { 199 throw new LoginException ("Invalid login module specified"); 200 } 201 JaasLoginModuleConfiguration config = context.getModules()[loginModuleIndex]; 202 LoginModule module = config.getLoginModule(classLoader); 203 context.getHandler().setExploring(); 205 try { 206 module.initialize(context.getSubject(), context.getHandler(), new HashMap (), config.getOptions()); 207 } catch (Exception e) { 208 System.err.println("Failed to initialize module"); 209 e.printStackTrace(); 210 } 211 try { 212 module.login(); 213 } catch (LoginException e) {} 214 try { 215 module.abort(); 216 } catch(LoginException e) {} 217 return context.getHandler().finalizeCallbackList(); 218 } 219 220 227 public boolean performServerLogin(JaasClientId userIdentifier, int loginModuleIndex, Callback [] results) throws LoginException { 228 JaasSecurityContext context = (JaasSecurityContext) activeLogins.get(userIdentifier); 229 if(context == null) { 230 throw new ExpiredLoginModuleException(); 231 } 232 if (loginModuleIndex < 0 || loginModuleIndex >= context.getModules().length || !context.getModules()[loginModuleIndex].isServerSide()) { 233 throw new LoginException ("Invalid login module specified"); 234 } 235 JaasLoginModuleConfiguration module = context.getModules()[loginModuleIndex]; 236 try { 237 context.getHandler().setClientResponse(results); 238 } catch (IllegalArgumentException iae) { 239 throw new LoginException (iae.toString()); 240 } 241 return module.getLoginModule(classLoader).login(); 242 } 243 244 250 public void clientLoginModuleCommit(JaasClientId userIdentifier, int loginModuleIndex, Principal [] clientLoginModulePrincipals) throws LoginException { 251 JaasSecurityContext context = (JaasSecurityContext) activeLogins.get(userIdentifier); 252 if(context == null) { 253 throw new ExpiredLoginModuleException(); 254 } 255 if(loginModuleIndex < 0 || loginModuleIndex >= context.getModules().length || context.getModules()[loginModuleIndex].isServerSide()) { 256 throw new LoginException ("Invalid login module specified"); 257 } 258 context.processPrincipals(clientLoginModulePrincipals, context.getModules()[loginModuleIndex].getLoginDomainName()); 259 } 260 261 267 public boolean serverLoginModuleCommit(JaasClientId userIdentifier, int loginModuleIndex) throws LoginException { 268 JaasSecurityContext context = (JaasSecurityContext) activeLogins.get(userIdentifier); 269 if(context == null) { 270 throw new ExpiredLoginModuleException(); 271 } 272 if(loginModuleIndex < 0 || loginModuleIndex >= context.getModules().length || !context.getModules()[loginModuleIndex].isServerSide()) { 273 throw new LoginException ("Invalid login module specified"); 274 } 275 JaasLoginModuleConfiguration module = context.getModules()[loginModuleIndex]; 276 boolean result = module.getLoginModule(classLoader).commit(); 277 context.processPrincipals(context.getModules()[loginModuleIndex].getLoginDomainName()); 278 return result; 279 } 280 281 285 public Principal [] loginSucceeded(JaasClientId userIdentifier) throws LoginException { 286 JaasSecurityContext context = (JaasSecurityContext) activeLogins.get(userIdentifier); 287 if(context == null) { 288 throw new ExpiredLoginModuleException(); 289 } 290 291 Subject subject = context.getSubject(); 292 ContextManager.registerSubject(subject); 293 SubjectId id = ContextManager.getSubjectId(subject); 294 IdentificationPrincipal principal = new IdentificationPrincipal(id); 295 subject.getPrincipals().add(principal); 296 SecurityRealm realm = getRealm(context.getRealmName()); 297 if(realm.isRestrictPrincipalsToServer()) { 298 return new Principal []{principal}; 299 } else { 300 List list = new ArrayList (); 301 list.addAll(context.getProcessedPrincipals()); 302 list.add(principal); 303 return (Principal []) list.toArray(new Principal [list.size()]); 304 } 305 } 306 307 311 public void loginFailed(JaasClientId userIdentifier) { 312 activeLogins.remove(userIdentifier); 313 } 314 315 319 public void logout(JaasClientId userIdentifier) throws LoginException { 320 JaasSecurityContext context = (JaasSecurityContext) activeLogins.get(userIdentifier); 321 if(context == null) { 322 throw new ExpiredLoginModuleException(); 323 } 324 ContextManager.unregisterSubject(context.getSubject()); 325 activeLogins.remove(userIdentifier); 326 for (int i = 0; i < context.getModules().length; i++) { 327 if(context.getModules()[i].isServerSide()) { 328 context.getModules()[i].getLoginModule(classLoader).logout(); 329 } 330 } 331 } 332 333 340 private JaasClientId initializeClient(SecurityRealm realm) { 341 long id; 342 synchronized(JaasLoginService.class) { 343 id = ++nextLoginModuleId; 344 } 345 JaasClientId clientId = new JaasClientId(id, hash(id)); 346 JaasLoginModuleConfiguration[] modules = realm.getAppConfigurationEntries(); 347 JaasSecurityContext context = new JaasSecurityContext(realm.getRealmName(), modules); 348 activeLogins.put(clientId, context); 349 return clientId; 350 } 351 352 private SecurityRealm getRealm(String realmName) { 353 for (Iterator it = realms.iterator(); it.hasNext();) { 354 SecurityRealm test = (SecurityRealm) it.next(); 355 if(test.getRealmName().equals(realmName)) { 356 return test; 357 } 358 } 359 return null; 360 } 361 362 367 private byte[] hash(long id) { 368 byte[] bytes = new byte[8]; 369 for (int i = 7; i >= 0; i--) { 370 bytes[i] = (byte) (id); 371 id >>>= 8; 372 } 373 374 try { 375 Mac mac = Mac.getInstance(algorithm); 376 mac.init(key); 377 mac.update(bytes); 378 379 return mac.doFinal(); 380 } catch (NoSuchAlgorithmException e) { 381 } catch (InvalidKeyException e) { 382 } 383 assert false : "Should never have reached here"; 384 return null; 385 } 386 387 388 389 static { 391 clockDaemon = new ClockDaemon(); 392 clockDaemon.setThreadFactory(new ThreadFactory() { 393 public Thread newThread(Runnable r) { 394 Thread t = new Thread (r, "LoginService login modules monitor"); 395 t.setDaemon(true); 396 return t; 397 } 398 }); 399 } 400 private class ExpirationMonitor implements Runnable { public void run() { 402 long now = System.currentTimeMillis(); 403 List list = new LinkedList (); 404 synchronized(activeLogins) { 405 for (Iterator it = activeLogins.keySet().iterator(); it.hasNext();) { 406 JaasClientId id = (JaasClientId) it.next(); 407 JaasSecurityContext context = (JaasSecurityContext) activeLogins.get(id); 408 int age = (int)(now-context.getCreated()); 409 if(context.isDone() || age > maxLoginDurationMillis) { 410 list.add(context); 411 context.setDone(true); 412 it.remove(); 413 } 414 } 415 } 416 for (Iterator it = list.iterator(); it.hasNext();) { 417 JaasSecurityContext context = (JaasSecurityContext) it.next(); 418 ContextManager.unregisterSubject(context.getSubject()); 419 } 420 } 421 } 422 423 424 425 public static final GBeanInfo GBEAN_INFO; 427 428 static { 429 GBeanInfoBuilder infoFactory = new GBeanInfoBuilder(JaasLoginService.class); 431 infoFactory.addAttribute("algorithm", String .class, true); 432 infoFactory.addAttribute("password", String .class, true); 433 infoFactory.addAttribute("classLoader", ClassLoader .class, false); 434 infoFactory.addAttribute("maxLoginDurationMillis", int.class, true); 435 infoFactory.addAttribute("expiredLoginScanIntervalMillis", int.class, true); 436 437 infoFactory.addOperation("connectToRealm", new Class []{String .class}); 438 infoFactory.addOperation("getLoginConfiguration", new Class []{JaasClientId.class}); 439 infoFactory.addOperation("getServerLoginCallbacks", new Class []{JaasClientId.class, int.class}); 440 infoFactory.addOperation("performServerLogin", new Class []{JaasClientId.class, int.class, Callback [].class}); 441 infoFactory.addOperation("clientLoginModuleCommit", new Class []{JaasClientId.class, int.class, Principal [].class}); 442 infoFactory.addOperation("serverLoginModuleCommit", new Class []{JaasClientId.class, int.class}); 443 infoFactory.addOperation("loginSucceeded", new Class []{JaasClientId.class}); 444 infoFactory.addOperation("loginFailed", new Class []{JaasClientId.class}); 445 infoFactory.addOperation("logout", new Class []{JaasClientId.class}); 446 447 infoFactory.addReference("Realms", SecurityRealm.class, NameFactory.SECURITY_REALM); 448 449 infoFactory.setConstructor(new String [] {"algorithm", "password", "classLoader"}); 450 451 GBEAN_INFO = infoFactory.getBeanInfo(); 452 } 453 454 public static GBeanInfo getGBeanInfo() { 455 return GBEAN_INFO; 456 } 457 } 458 | Popular Tags |