1 17 18 package org.apache.geronimo.security.jaas.server; 19 20 import org.apache.commons.logging.Log; 21 import org.apache.commons.logging.LogFactory; 22 import org.apache.geronimo.common.GeronimoSecurityException; 23 import org.apache.geronimo.gbean.GBeanInfo; 24 import org.apache.geronimo.gbean.GBeanInfoBuilder; 25 import org.apache.geronimo.gbean.GBeanLifecycle; 26 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory; 27 import org.apache.geronimo.security.ContextManager; 28 import org.apache.geronimo.security.IdentificationPrincipal; 29 import org.apache.geronimo.security.SubjectId; 30 import org.apache.geronimo.security.jaas.LoginUtils; 31 import org.apache.geronimo.security.realm.SecurityRealm; 32 33 import javax.crypto.Mac; 34 import javax.crypto.SecretKey; 35 import javax.crypto.spec.SecretKeySpec; 36 37 import javax.security.auth.Subject ; 38 import javax.security.auth.callback.Callback ; 39 import javax.security.auth.login.LoginException ; 40 import javax.security.auth.spi.LoginModule ; 41 import java.security.InvalidKeyException ; 42 import java.security.NoSuchAlgorithmException ; 43 import java.security.Principal ; 44 45 import java.util.Collection ; 46 import java.util.HashMap ; 47 import java.util.Hashtable ; 48 import java.util.Iterator ; 49 import java.util.LinkedList ; 50 import java.util.List ; 51 import java.util.Map ; 52 import java.util.Set ; 53 import java.util.Timer ; 54 import java.util.TimerTask ; 55 56 64 public class JaasLoginService implements GBeanLifecycle, JaasLoginServiceMBean { 65 public static final Log log = LogFactory.getLog(JaasLoginService.class); 66 private final static int DEFAULT_EXPIRED_LOGIN_SCAN_INTERVAL = 300000; private final static int DEFAULT_MAX_LOGIN_DURATION = 1000 * 3600 * 24; private final static Timer clockDaemon = new Timer ( true); 69 private static long nextLoginModuleId = System.currentTimeMillis(); 70 private Collection realms; 71 private final String objectName; 72 private final SecretKey key; 73 private final String algorithm; 74 private final ClassLoader classLoader; 75 private final Map activeLogins = new Hashtable (); 76 private int expiredLoginScanIntervalMillis = DEFAULT_EXPIRED_LOGIN_SCAN_INTERVAL; 77 private int maxLoginDurationMillis = DEFAULT_MAX_LOGIN_DURATION; 78 private ExpirationMonitor expirationMonitor; 79 80 public JaasLoginService(String algorithm, String password, ClassLoader classLoader, String objectName) { 81 this.classLoader = classLoader; 82 this.algorithm = algorithm; 83 key = new SecretKeySpec(password.getBytes(), algorithm); 84 this.objectName = objectName; 85 } 86 87 public String getObjectName() { 88 return objectName; 89 } 90 91 94 public Collection getRealms() { 95 return realms; 96 } 97 98 101 public void setRealms(Collection realms) { 102 this.realms = realms; 103 } 105 106 109 public int getMaxLoginDurationMillis() { 110 return maxLoginDurationMillis; 111 } 112 113 116 public void setMaxLoginDurationMillis(int maxLoginDurationMillis) { 117 if (maxLoginDurationMillis == 0) { 118 maxLoginDurationMillis = DEFAULT_MAX_LOGIN_DURATION; 119 } 120 this.maxLoginDurationMillis = maxLoginDurationMillis; 121 } 122 123 126 public int getExpiredLoginScanIntervalMillis() { 127 return expiredLoginScanIntervalMillis; 128 } 129 130 133 public void setExpiredLoginScanIntervalMillis(int expiredLoginScanIntervalMillis) { 134 if (expiredLoginScanIntervalMillis == 0) { 135 expiredLoginScanIntervalMillis = DEFAULT_EXPIRED_LOGIN_SCAN_INTERVAL; 136 } 137 this.expiredLoginScanIntervalMillis = expiredLoginScanIntervalMillis; 138 } 139 140 public void doStart() throws Exception { 141 expirationMonitor = new ExpirationMonitor(); 142 143 clockDaemon.scheduleAtFixedRate( 144 expirationMonitor, expiredLoginScanIntervalMillis, expiredLoginScanIntervalMillis); 145 } 146 147 public void doStop() throws Exception { 148 if (expirationMonitor != null) { 149 expirationMonitor.cancel(); 150 expirationMonitor = null; 151 } 152 153 } 155 156 public void doFail() { 157 } 159 160 169 public JaasSessionId connectToRealm(String realmName) { 170 SecurityRealm realm; 171 realm = getRealm(realmName); 172 if (realm == null) { 173 throw new GeronimoSecurityException("No such realm (" + realmName + ")"); 174 } else { 175 return initializeClient(realm); 176 } 177 } 178 179 183 public JaasLoginModuleConfiguration[] getLoginConfiguration(JaasSessionId sessionHandle) throws LoginException { 184 JaasSecuritySession session = (JaasSecuritySession) activeLogins.get(sessionHandle); 185 if (session == null) { 186 throw new ExpiredLoginModuleException(); 187 } 188 JaasLoginModuleConfiguration[] config = session.getModules(); 189 JaasLoginModuleConfiguration[] result = new JaasLoginModuleConfiguration[config.length]; 191 for (int i = 0; i < config.length; i++) { 192 result[i] = LoginUtils.getSerializableCopy(config[i]); 193 } 194 return result; 195 } 196 197 204 public Callback [] getServerLoginCallbacks(JaasSessionId sessionHandle, int loginModuleIndex) throws LoginException { 205 JaasSecuritySession session = (JaasSecuritySession) activeLogins.get(sessionHandle); 206 checkContext(session, loginModuleIndex); 207 LoginModule module = session.getLoginModule(loginModuleIndex); 208 209 session.getHandler().setExploring(); 210 try { 211 module.initialize(session.getSubject(), session.getHandler(), new HashMap (), session.getOptions(loginModuleIndex)); 212 } catch (Exception e) { 213 log.error("Failed to initialize module", e); 214 } 215 try { 216 module.login(); 217 } catch (LoginException e) { 218 } 220 try { 221 module.abort(); 222 } catch (LoginException e) { 223 } 225 return session.getHandler().finalizeCallbackList(); 226 } 227 228 235 public boolean performLogin(JaasSessionId sessionHandle, int loginModuleIndex, Callback [] results) throws LoginException { 236 JaasSecuritySession session = (JaasSecuritySession) activeLogins.get(sessionHandle); 237 checkContext(session, loginModuleIndex); 238 try { 239 session.getHandler().setClientResponse(results); 240 } catch (IllegalArgumentException iae) { 241 throw new LoginException (iae.toString()); 242 } 243 return session.getLoginModule(loginModuleIndex).login(); 244 } 245 246 252 public boolean performCommit(JaasSessionId sessionHandle, int loginModuleIndex) throws LoginException { 253 JaasSecuritySession session = (JaasSecuritySession) activeLogins.get(sessionHandle); 254 checkContext(session, loginModuleIndex); 255 return session.getLoginModule(loginModuleIndex).commit(); 256 } 257 258 262 public boolean performAbort(JaasSessionId sessionHandle, int loginModuleIndex) throws LoginException { 263 JaasSecuritySession session = (JaasSecuritySession) activeLogins.get(sessionHandle); 264 checkContext(session, loginModuleIndex); 265 return session.getLoginModule(loginModuleIndex).abort(); 266 } 267 268 272 public Principal loginSucceeded(JaasSessionId sessionHandle) throws LoginException { 273 JaasSecuritySession session = (JaasSecuritySession) activeLogins.get(sessionHandle); 274 if (session == null) { 275 throw new ExpiredLoginModuleException(); 276 } 277 278 Subject subject = session.getSubject(); 279 ContextManager.registerSubject(subject); 280 SubjectId id = ContextManager.getSubjectId(subject); 281 IdentificationPrincipal principal = new IdentificationPrincipal(id); 282 subject.getPrincipals().add(principal); 283 return principal; 284 } 285 286 290 public void loginFailed(JaasSessionId sessionHandle) { 291 activeLogins.remove(sessionHandle); 292 } 293 294 298 public void logout(JaasSessionId sessionHandle) throws LoginException { 299 JaasSecuritySession session = (JaasSecuritySession) activeLogins.get(sessionHandle); 300 if (session == null) { 301 throw new ExpiredLoginModuleException(); 302 } 303 ContextManager.unregisterSubject(session.getSubject()); 304 activeLogins.remove(sessionHandle); 305 for (int i = 0; i < session.getModules().length; i++) { 306 if (session.isServerSide(i)) { 307 session.getLoginModule(i).logout(); 308 } 309 } 310 } 311 312 320 public Map syncShareState(JaasSessionId sessionHandle, Map sharedState) throws LoginException { 321 JaasSecuritySession session = (JaasSecuritySession) activeLogins.get(sessionHandle); 322 if (session == null) { 323 throw new ExpiredLoginModuleException(); 324 } 325 session.getSharedContext().putAll(sharedState); 326 return LoginUtils.getSerializableCopy(session.getSharedContext()); 327 } 328 329 337 public Set syncPrincipals(JaasSessionId sessionHandle, Set principals) throws LoginException { 338 JaasSecuritySession session = (JaasSecuritySession) activeLogins.get(sessionHandle); 339 if (session == null) { 340 throw new ExpiredLoginModuleException(); 341 } 342 session.getSubject().getPrincipals().addAll(principals); 343 344 return LoginUtils.getSerializableCopy(session.getSubject().getPrincipals()); 345 } 346 347 private void checkContext(JaasSecuritySession session, int loginModuleIndex) throws LoginException { 348 if (session == null) { 349 throw new ExpiredLoginModuleException(); 350 } 351 if (loginModuleIndex < 0 || loginModuleIndex >= session.getModules().length || !session.isServerSide(loginModuleIndex)) { 352 throw new LoginException ("Invalid login module specified"); 353 } 354 } 355 356 363 private JaasSessionId initializeClient(SecurityRealm realm) { 364 long id; 365 synchronized (JaasLoginService.class) { 366 id = ++nextLoginModuleId; 367 } 368 JaasSessionId sessionHandle = new JaasSessionId(id, hash(id)); 369 JaasLoginModuleConfiguration[] modules = realm.getAppConfigurationEntries(); 370 JaasSecuritySession session = new JaasSecuritySession(realm.getRealmName(), modules, new HashMap (), classLoader); 371 activeLogins.put(sessionHandle, session); 372 return sessionHandle; 373 } 374 375 private SecurityRealm getRealm(String realmName) { 376 for (Iterator it = realms.iterator(); it.hasNext();) { 377 SecurityRealm test = (SecurityRealm) it.next(); 378 if (test.getRealmName().equals(realmName)) { 379 return test; 380 } 381 } 382 return null; 383 } 384 385 390 private byte[] hash(long id) { 391 byte[] bytes = new byte[8]; 392 for (int i = 7; i >= 0; i--) { 393 bytes[i] = (byte) (id); 394 id >>>= 8; 395 } 396 397 try { 398 Mac mac = Mac.getInstance(algorithm); 399 mac.init(key); 400 mac.update(bytes); 401 402 return mac.doFinal(); 403 } catch (NoSuchAlgorithmException e) { 404 } catch (InvalidKeyException e) { 405 } 406 assert false : "Should never have reached here"; 407 return null; 408 } 409 410 private class ExpirationMonitor extends TimerTask { 412 public void run() { 413 long now = System.currentTimeMillis(); 414 List list = new LinkedList (); 415 synchronized (activeLogins) { 416 for (Iterator it = activeLogins.keySet().iterator(); it.hasNext();) { 417 JaasSessionId id = (JaasSessionId) it.next(); 418 JaasSecuritySession session = (JaasSecuritySession) activeLogins.get(id); 419 int age = (int) (now - session.getCreated()); 420 if (session.isDone() || age > maxLoginDurationMillis) { 421 list.add(session); 422 session.setDone(true); 423 it.remove(); 424 } 425 } 426 } 427 for (Iterator it = list.iterator(); it.hasNext();) { 428 JaasSecuritySession session = (JaasSecuritySession) it.next(); 429 ContextManager.unregisterSubject(session.getSubject()); 430 } 431 } 432 } 433 434 435 public static final GBeanInfo GBEAN_INFO; 437 438 static { 439 GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(JaasLoginService.class, "JaasLoginService"); 440 441 infoFactory.addAttribute("algorithm", String .class, true); 442 infoFactory.addAttribute("password", String .class, true); 443 infoFactory.addAttribute("classLoader", ClassLoader .class, false); 444 infoFactory.addAttribute("maxLoginDurationMillis", int.class, true); 445 infoFactory.addAttribute("expiredLoginScanIntervalMillis", int.class, true); 446 infoFactory.addAttribute("objectName", String .class, false); 447 448 infoFactory.addOperation("connectToRealm", new Class []{String .class}); 449 infoFactory.addOperation("getLoginConfiguration", new Class []{JaasSessionId.class}); 450 infoFactory.addOperation("getServerLoginCallbacks", new Class []{JaasSessionId.class, int.class}); 451 infoFactory.addOperation("performLogin", new Class []{JaasSessionId.class, int.class, Callback [].class}); 452 infoFactory.addOperation("performCommit", new Class []{JaasSessionId.class, int.class}); 453 infoFactory.addOperation("loginSucceeded", new Class []{JaasSessionId.class}); 454 infoFactory.addOperation("loginFailed", new Class []{JaasSessionId.class}); 455 infoFactory.addOperation("logout", new Class []{JaasSessionId.class}); 456 infoFactory.addOperation("syncShareState", new Class []{JaasSessionId.class, Map .class}); 457 infoFactory.addOperation("syncPrincipals", new Class []{JaasSessionId.class, Set .class}); 458 459 infoFactory.addReference("Realms", SecurityRealm.class, NameFactory.SECURITY_REALM); 460 infoFactory.addInterface(JaasLoginServiceMBean.class); 461 462 infoFactory.setConstructor(new String []{"algorithm", "password", "classLoader", "objectName"}); 463 464 GBEAN_INFO = infoFactory.getBeanInfo(); 465 } 466 467 public static GBeanInfo getGBeanInfo() { 468 return GBEAN_INFO; 469 } 470 } 471 | Popular Tags |