README for the BEEPLib SASL Profiles

This BEEP library comes equipped with two SASL profiles, corresponding to the Anonymous and OTP SASL mechanisms. Example code for the use of both mechanisms has been included in the distribution in org.beepcore.beep.example.SASLExample. This document will describe this example and extrapolate from it to assist in the development and deployment of other BEEP-based applications that wish to take advanatage of the SASL mechanisms provided.

An explicit explanation of the SASLExample:

The code that follows has been edited slightly to focus on the important points. Commentary can be found in bold and preceeds the logic it's describing.

public class SASLExample

{

/* Package and import directives omitted for brevity. Local constants denoted in UPPER_CASE*/

> public static void main(String argv[])
> {

> > /* BEEPLib's Logging facility is initialized here, we use the default console log (maps to System.out). */
> > ConsoleLog log = new ConsoleLog();
> > log.setSeverity(Log.SEV_DEBUG_VERBOSE);
> > Log.setLogService(log);

> > try
> > {

> > /* ProfileRegistry is a helpful class. It allows a BEEP Peer to register various classes (implementations of the class ChannelControlListener) to process StartChannel requests for a given profile. In this case, we're creating and initializing instances of our two SASL profiles, Anonymous and OTP. We then register these profiles (which are their own Channel Control Listeners) as the class to be called if a start channel request is received. We also register the EchoProfile (for reasons we'll show later). We register CCLs for each profile with the addChannelControlListener call */
> > // Set up our profile registry
> > ProfileRegistry reg = new ProfileRegistry();
> > SASLAnonymousProfile anon = new SASLAnonymousProfile();
> > SASLOTPProfile otp = new SASLOTPProfile();
> > anon.init(new ProfileConfiguration());
> > otp.init(new ProfileConfiguration());
> > reg.addChannelControlListener(SASLOTPProfile.uri, otp);
> > reg.addChannelControlListener(SASLAnonymousProfile.uri, anon);
> > reg.addChannelControlListener(EchoProfile.uri,
> > new SASLExample().getCCL());

> > /* If the command line arguments specify to make this a 'listener' then we proceed to set ourselves up appropriately. We have provided a simple stub routine to generate OTP databases for some sample users (which are IW_User and IW_User2) to allow the example to function correctly.*/

> > // If we're a listener, then create sessions by 'listening' on the
> > // static AutomatedTCPSessionCreator methods
> > if (argv[2].charAt(0) == 'l') {
> > InetAddress addr = null;

> > /* This bit is a little inane, it simple creates a couple of OTP database files for IW_User and IW_User2. After running this in listening mode, you can see this files locally. I suggest you take a look at them. It is our intention to write an interface through which other storage mechanisms can hook in to store these things - so that the profile can be used without being extended. That's rife with security issues however, so this is what is available for now.*/

> > // Creates stub accts for the users in this example
> > try
> > {
> > UserDatabasePool.populateUserDatabases();
> > }
> > catch(BEEPException ex)
> > {
> > ex.printStackTrace();
> > }

> > /* This is where we bind to an address/port combination and begin to listen for connections. If another peer connects to our port and sends us a greeting, a new session is created.*/

> > try {
> > addr = InetAddress.getByName(argv[1]);
> > } catch (Exception x) {
> > addr = InetAddress.getLocalHost();
> > }

> > System.out.println("Listening on " + addr.toString() + ":"
> > + argv[1]);

> > while (true) {
> > Session newSession = AutomatedTCPSessionCreator.listen(addr,
> > Integer.parseInt(argv[1]),
> > reg);

> > }
> > }

> > /* This is the initiator path */

> > // Otherwise we're initiating.
> > else if (argv[2].charAt(0) == 'i')
> > {
> > Channel echoChannel = null;
> > Log.logEntry(Log.SEV_DEBUG,"Initiating..."
> > + InetAddress.getByName(argv[0]).toString()
> > + ":" + argv[1]);

> > /* We create a Session by connecting to the host/port where we know another BEEP peer is listening. The peers exchange greetings, and a reference to the Session is returned to us */

> > Session session = AutomatedTCPSessionCreator.initiate(InetAddress.getByName(argv[0]),
> > Integer.parseInt(argv[1]),
> > reg);

> > /* The routine used below, AuthenticateSASLAnonymous is provided as a convenience routine. All you have to do is provide it with your session (the first argument) and some sort of identifier for yourself (the second argument, in this case, the 'anonymous' string constant) and go. The return value is another Session reference. In this case, this will be the same session - although this isn't always the case.*/
> > if(argv[3].charAt(0) == 'a')
> > {
> > session = SASLAnonymousProfile.AuthenticateSASLAnonymous(session,
> > SASLAnonymousProfile.ANONYMOUS);
> > }

> > /* This is the OTP convenience routine. The arguments are, first, the session you wish to Authenticate on, second the Authorization ID you wish to use (this is who you are authorized to act as, which is different than who you authenticate as - see the OTP spec for a more extensive explanation), third, the identity you're authenticating as, fourth, the passphrase you use to secure yourself.*/
> > else if(argv[3].charAt(0) == 'o')
> > {
> > session = (TCPSession)SASLOTPProfile.AuthenticateSASLOTP(session,
> > null, // No Authorization ID
> > SAMPLE_OTP_USER,
> > SAMPLE_OTP_PASSPHRASE );
> > }

> > /* When either SASL authentication routine succeeds without throwing a SASLException, you're homefree! We can examine the new credentials we have by calling session->getMyCredentials() which returns a SessionCredential object, whose toString() method prettyily prints all its attributes.*/

> > /* What we're going to do now shows us who we are as far as our peer is concerned. We're going to start up an ECHO profile on BEEP (a profile that simply echos back what we type) only it has a twist. The ECHO profile this time (since we're running our example) echoes back what we sent them AND includes credential information indicating who we have authenticated as - from their point of view. This shows how the credential exists on both sides.*/
> > echoChannel = session.startChannel(EchoProfile.uri);
> > String temp = "Hi There!";
> > ReplyListener idiot = null;
> > echoChannel.sendMSG(new StringDataStream(temp),idiot);
> > Utility.delay(500);
> > }
> > else
> > {
> > System.out.println(USAGE);
> > return;
> > }
> > } catch (Exception x) {
> > x.printStackTrace();
> > Log.logEntry(Log.SEV_ERROR,"SASL Example Failed. Exiting.");
> > return;
> > }
> > Log.logEntry(Log.SEV_DEBUG,"SASL Example Succeeded. Exiting");

> }
> /* The funky echo described above is implemented in the SASLExample source code. You can examine it there if you like, but it is merely a distraction here. */

To use the existing SASLAnonymous and SASLOTP Profiles:

Simply initiate using the various Authenticate routines in the profiles, or listen using an org.beepcore.beep.core.ProfileRegistry which registers the included implmentations of SASLOTP and SASLAnonymous as the CCLs (ChannelControlListeners) for the uri's corresponding to the two SASL Profiles. The specific classes are org.beepcore.profile.sasl.anonymous.SASLAnonymousProfile and org.beepcore.profile.sasl.otp.SASLOTPProfile of course.

Example:

To Authenticate to another BEEP peer using Anonymous SASL:

simply call AuthenticateSASLAnonymous in or g.beepcore.profile.sasl.anonymous.SASLAnonymousProfile with an existing session, such as is done below.

// We have a valid Session 'mySession'

mySession = SASLAnonymousProfile.AuthenticateSASLAnonymous( mySession, null, "Bob", "Bob's_password");

To Authenticate to another BEEP peer using OTP SASL:

The OTP case is very similar to the Anonymous one

// We have a valid Session 'mySession'

mySession = SASLAnonymousProfile.AuthenticateSASLOTP( mySession, null, "Bob", "Bob's_password");

To access the Credentials associated ith a given Session:

SessionCredential myCredentials = mySession.getLocalCredential();

SessionCredential myPeersCredentials = mySession.getPeerCredential();

System.out.println("My credentials are "+myCredentials.toString());

System.out.println("My peer's credentials are "+myPeersCredentials.toString());

Note: The SessionCredential class has several data members, each of which can be accessed as outlined in the documentation for org.beepcore.beep.core.SessionCredential.

To generate a sequence of One-Time Passwords

A utility class org.beepcore.beep.profile.sasl.otp.Generator has been provided for the purpose of generating OTP databases for individual users. These databases take the form of flat files that are the serialized output of the java.util.Properties class. The library does not make an effort to modify or maintain any sort of access control for the generated files. It is up to the application or the individual user to protect access to the generated file. It will disallow the generation of a user otp database file on top of a previously generated one.

To use: simply run java org.beepcore.beep.profile.sasl.otp.Generator. This will bring up a series of questions and prompts that will allow the user to enter enough information to create an OTP database. The generated file will then be produced in the local directory. It is the responsiblity of the user to ensure that the generated file is accessible by listening BEEP peers in order to facilitate OTP authentication.

The 'clean' way to renew or alter them is to use the AuthenticateSASLOTPWithInit call in org.beepcore.beep.profile.sasl.otp.SASLOTPProfile which is identical to the other OTP authentication calls, except that it tells the server to create a record for this user. If you have access to the test directory, OTPTest shows how to do this too.

Note however that the whole issue of how the user information for OTP is stored and propagated is all very murky. The org.beepcore.beep.profile.sasl.otp.UserDatabase interface is designed to kind of provide room for expansion in this area, considering the ability to create or delete user OTP databases, and the like. We will eventually have to provide some sort of facility for this in the BEEP libraries, not in core, but in lib perhaps. Until then, look to UserDatabaseManager and its implementation in UserDatabasePool as the prototype.