001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.security.srp;
023:
024: import java.lang.reflect.Method;
025: import java.lang.reflect.InvocationTargetException;
026: import java.lang.reflect.Proxy;
027: import java.lang.reflect.UndeclaredThrowableException;
028: import java.rmi.server.RMIClientSocketFactory;
029: import java.rmi.server.RMIServerSocketFactory;
030: import java.util.Collections;
031: import java.util.HashMap;
032: import java.util.Map;
033: import javax.naming.InitialContext;
034: import javax.naming.Name;
035: import javax.naming.NamingException;
036:
037: import org.jboss.invocation.Invocation;
038: import org.jboss.invocation.MarshalledInvocation;
039: import org.jboss.naming.NonSerializableFactory;
040: import org.jboss.security.srp.SRPRemoteServer;
041: import org.jboss.security.srp.SRPServerListener;
042: import org.jboss.security.srp.SRPServerInterface;
043: import org.jboss.security.srp.SRPServerSession;
044: import org.jboss.security.srp.SRPVerifierStore;
045: import org.jboss.system.ServiceMBeanSupport;
046: import org.jboss.util.CachePolicy;
047: import org.jboss.util.TimedCachePolicy;
048:
049: /** The JMX mbean interface for the SRP service. This mbean sets up an
050: RMI implementation of the 'Secure Remote Password' cryptographic authentication
051: system described in RFC2945.
052:
053: @author Scott.Stark@jboss.org
054: @version $Revision: 57210 $
055: */
056: public class SRPService extends ServiceMBeanSupport implements
057: SRPServiceMBean, SRPServerListener {
058: /**
059: * @supplierRole RMI Access
060: * @supplierCardinality 1
061: * @clientCardinality 1
062: * @clientRole service mangement
063: */
064: private SRPRemoteServer server;
065: private int serverPort = 10099;
066:
067: /**
068: * @supplierRole password store
069: * @supplierCardinality 1
070: * @clientRole configures
071: */
072: private SRPVerifierStore verifierStore;
073: private String verifierSourceJndiName = "srp/DefaultVerifierSource";
074: private String serverJndiName = "srp/SRPServerInterface";
075: private String cacheJndiName = "srp/AuthenticationCache";
076: private CachePolicy cachePolicy;
077: private int cacheTimeout = 1800;
078: private int cacheResolution = 60;
079: /** A flag indicating if a successful user auth for an existing session
080: should overwrite the current session.
081: */
082: private boolean overwriteSessions;
083: /** A flag indicating if an aux challenge must be presented in verify */
084: private boolean requireAuxChallenge;
085: /** An optional custom client socket factory */
086: private RMIClientSocketFactory clientSocketFactory;
087: /** An optional custom server socket factory */
088: private RMIServerSocketFactory serverSocketFactory;
089: /** The class name of the optional custom client socket factory */
090: private String clientSocketFactoryName;
091: /** The class name of the optional custom server socket factory */
092: private String serverSocketFactoryName;
093: /** A <Long,Method> mapping of the SRPRemoteServerInterface */
094: private Map marshalledInvocationMapping = new HashMap();
095:
096: // --- Begin SRPServiceMBean interface methods
097: /** Get the jndi name for the SRPVerifierSource implementation binding.
098: */
099: public String getVerifierSourceJndiName() {
100: return verifierSourceJndiName;
101: }
102:
103: /** set the jndi name for the SRPVerifierSource implementation binding.
104: */
105: public void setVerifierSourceJndiName(String jndiName) {
106: this .verifierSourceJndiName = jndiName;
107: }
108:
109: /** Get the jndi name under which the SRPServerInterface proxy should be bound
110: */
111: public String getJndiName() {
112: return serverJndiName;
113: }
114:
115: /** Set the jndi name under which the SRPServerInterface proxy should be bound
116: */
117: public void setJndiName(String jndiName) {
118: this .serverJndiName = jndiName;
119: }
120:
121: /** Get the jndi name under which the SRPServerInterface proxy should be bound
122: */
123: public String getAuthenticationCacheJndiName() {
124: return cacheJndiName;
125: }
126:
127: /** Set the jndi name under which the SRPServerInterface proxy should be bound
128: */
129: public void setAuthenticationCacheJndiName(String jndiName) {
130: this .cacheJndiName = jndiName;
131: }
132:
133: /** Get the auth cache timeout period in seconds
134: */
135: public int getAuthenticationCacheTimeout() {
136: return cacheTimeout;
137: }
138:
139: /** Set the auth cache timeout period in seconds
140: */
141: public void setAuthenticationCacheTimeout(int timeoutInSecs) {
142: this .cacheTimeout = timeoutInSecs;
143: }
144:
145: /** Get the auth cache resolution period in seconds
146: */
147: public int getAuthenticationCacheResolution() {
148: return cacheResolution;
149: }
150:
151: /** Set the auth cache resolution period in seconds
152: */
153: public void setAuthenticationCacheResolution(int resInSecs) {
154: this .cacheResolution = resInSecs;
155: }
156:
157: public boolean getRequireAuxChallenge() {
158: return this .requireAuxChallenge;
159: }
160:
161: public void setRequireAuxChallenge(boolean flag) {
162: this .requireAuxChallenge = flag;
163: }
164:
165: public boolean getOverwriteSessions() {
166: return this .overwriteSessions;
167: }
168:
169: public void setOverwriteSessions(boolean flag) {
170: this .overwriteSessions = flag;
171: }
172:
173: /** Get the RMIClientSocketFactory implementation class. If null the default
174: RMI client socket factory implementation is used.
175: */
176: public String getClientSocketFactory() {
177: return serverSocketFactoryName;
178: }
179:
180: /** Set the RMIClientSocketFactory implementation class. If null the default
181: RMI client socket factory implementation is used.
182: */
183: public void setClientSocketFactory(String factoryClassName)
184: throws ClassNotFoundException, InstantiationException,
185: IllegalAccessException {
186: this .clientSocketFactoryName = factoryClassName;
187: ClassLoader loader = Thread.currentThread()
188: .getContextClassLoader();
189: Class clazz = loader.loadClass(clientSocketFactoryName);
190: clientSocketFactory = (RMIClientSocketFactory) clazz
191: .newInstance();
192: }
193:
194: /** Get the RMIServerSocketFactory implementation class. If null the default
195: RMI server socket factory implementation is used.
196: */
197: public String getServerSocketFactory() {
198: return serverSocketFactoryName;
199: }
200:
201: /** Set the RMIServerSocketFactory implementation class. If null the default
202: RMI server socket factory implementation is used.
203: */
204: public void setServerSocketFactory(String factoryClassName)
205: throws ClassNotFoundException, InstantiationException,
206: IllegalAccessException {
207: this .serverSocketFactoryName = factoryClassName;
208: ClassLoader loader = Thread.currentThread()
209: .getContextClassLoader();
210: Class clazz = loader.loadClass(serverSocketFactoryName);
211: serverSocketFactory = (RMIServerSocketFactory) clazz
212: .newInstance();
213: }
214:
215: /** Get the RMI port for the SRPServerInterface
216: */
217: public int getServerPort() {
218: return serverPort;
219: }
220:
221: /** Get the RMI port for the SRPServerInterface
222: */
223: public void setServerPort(int serverPort) {
224: this .serverPort = serverPort;
225: }
226:
227: // --- End SRPServiceMBean interface methods
228:
229: /** Called when username has sucessfully completed the SRP login. This
230: places the SRP session into the credential cache using a
231: SimplePrincipal based on the username as the key.
232: */
233: public void verifiedUser(SRPSessionKey key, SRPServerSession session) {
234: try {
235: synchronized (cachePolicy) {
236: // We only insert a principal if there is no current entry.
237: if (cachePolicy.peek(key) == null) {
238: cachePolicy.insert(key, session);
239: log.trace("Cached SRP session for user=" + key);
240: } else if (overwriteSessions) {
241: cachePolicy.remove(key);
242: cachePolicy.insert(key, session);
243: log.trace("Replaced SRP session for user=" + key);
244: } else {
245: log
246: .debug("Ignoring SRP session due to existing session for user="
247: + key);
248: }
249: }
250: } catch (Exception e) {
251: log.error("Failed to update SRP cache for user=" + key, e);
252: }
253: }
254:
255: public void closedUserSession(SRPSessionKey key) {
256: try {
257: synchronized (cachePolicy) {
258: // We only insert a principal if there is no current entry.
259: if (cachePolicy.peek(key) == null) {
260: log.warn("No SRP session found for user=" + key);
261: }
262: cachePolicy.remove(key);
263: }
264: } catch (Exception e) {
265: log.error("Failed to update SRP cache for user=" + key, e);
266: }
267: }
268:
269: public String getName() {
270: return "SRPService";
271: }
272:
273: public Object invoke(Invocation invocation) throws Exception {
274: // Set the method hash to Method mapping
275: if (invocation instanceof MarshalledInvocation) {
276: MarshalledInvocation mi = (MarshalledInvocation) invocation;
277: mi.setMethodMap(marshalledInvocationMapping);
278: }
279: // Invoke the SRPRemoteServer method via reflection
280: Method method = invocation.getMethod();
281: Object[] args = invocation.getArguments();
282: Object value = null;
283: try {
284: value = method.invoke(server, args);
285: } catch (InvocationTargetException e) {
286: Throwable t = e.getTargetException();
287: if (t instanceof Exception)
288: throw (Exception) t;
289: else
290: throw new UndeclaredThrowableException(t, method
291: .toString());
292: }
293:
294: return value;
295: }
296:
297: protected void startService() throws Exception {
298: loadStore();
299: server = new SRPRemoteServer(verifierStore, serverPort,
300: clientSocketFactory, serverSocketFactory);
301: server.addSRPServerListener(this );
302: server.setRequireAuxChallenge(this .requireAuxChallenge);
303:
304: // Bind a proxy to the SRPRemoteServer into jndi
305: InitialContext ctx = new InitialContext();
306: if (serverJndiName != null && serverJndiName.length() > 0) {
307: SRPServerProxy proxyHandler = new SRPServerProxy(server);
308: ClassLoader loader = Thread.currentThread()
309: .getContextClassLoader();
310: Class[] interfaces = { SRPServerInterface.class };
311: Object proxy = Proxy.newProxyInstance(loader, interfaces,
312: proxyHandler);
313: org.jboss.naming.Util.rebind(ctx, serverJndiName, proxy);
314: log.debug("Bound SRPServerProxy at " + serverJndiName);
315: }
316:
317: // First check for an existing CachePolicy binding
318: try {
319: cachePolicy = (CachePolicy) ctx.lookup(cacheJndiName);
320: log.debug("Found AuthenticationCache at: " + cacheJndiName);
321: } catch (Exception e) {
322: log.trace("Failed to find existing cache at: "
323: + cacheJndiName, e);
324: // Not found, default to a TimedCachePolicy
325: cachePolicy = new TimedCachePolicy(cacheTimeout, true,
326: cacheResolution);
327: cachePolicy.create();
328: cachePolicy.start();
329: // Bind a reference to store using NonSerializableFactory as the ObjectFactory
330: Name name = ctx.getNameParser("").parse(cacheJndiName);
331: NonSerializableFactory.rebind(name, cachePolicy, true);
332: log.debug("Bound AuthenticationCache at " + cacheJndiName);
333: }
334:
335: // Build the SRPRemoteServerInterface method map
336: HashMap tmpMap = new HashMap(13);
337: Method[] methods = SRPRemoteServerInterface.class.getMethods();
338: for (int m = 0; m < methods.length; m++) {
339: Method method = methods[m];
340: Long hash = new Long(MarshalledInvocation
341: .calculateHash(method));
342: tmpMap.put(hash, method);
343: }
344: marshalledInvocationMapping = Collections
345: .unmodifiableMap(tmpMap);
346: }
347:
348: protected void stopService() throws Exception {
349: // Bind a reference to store using NonSerializableFactory as the ObjectFactory
350: InitialContext ctx = new InitialContext();
351: ctx.unbind(serverJndiName);
352: log.debug("Unbound SRPServerProxy at " + serverJndiName);
353: NonSerializableFactory.unbind(cacheJndiName);
354: ctx.unbind(cacheJndiName);
355: log.debug("Unbound AuthenticationCache at " + cacheJndiName);
356: }
357:
358: private void loadStore() throws NamingException {
359: InitialContext ctx = new InitialContext();
360: // Get the SRPVerifierStore implementation
361: verifierStore = (SRPVerifierStore) ctx
362: .lookup(verifierSourceJndiName);
363: if (server != null) {
364: server.setVerifierStore(verifierStore);
365: }
366: }
367:
368: }
|