Java Dynamic Management Kit 5.1 Tutorial

11.4.2 SASL Provider

In addition to the simple SASL security and the privacy-based security you can apply to JMXMP connectors, you can also define a custom SASL provider mechanism by defining your own SASL Provider.

The SASL mechanism used in this example is a straightforward customized mechanism called SAMPLE SASL. The example is found in the subdirectories of examplesDir/current/Security/jmxmp/sasl_provider. The SAMPLE SASL mechanism is defined in the SampleServer class, shown below.


Example 11–11 Custom SASL Server

import java.io.*;
import javax.security.sasl.*;

public class SampleServer implements SaslServer {

    private String username;
    private boolean completed = false;

    public String getMechanismName() {
        return "SAMPLE";
    }

    public byte[] evaluateResponse(byte[] responseData) 
            throws SaslException {
        if (completed) {
            throw new SaslException("Already completed");
        }
        completed = true;
        username = new String(responseData);
        return null;
    }

    public String getAuthorizationID() {
        return username;
    }

    public boolean isComplete() {
        return completed;
    }

    public byte[] unwrap(byte[] incoming, int offset, int len) 
        throws SaslException {
        throw new SaslException("No negotiated security layer");
    }

    public byte[] wrap(byte[] outgoing, int offset, int len)
        throws SaslException {
        throw new SaslException("No negotiated security layer");
    }

    public Object getNegotiatedProperty(String propName) {
        if (propName.equals(Sasl.QOP)) {
            return "auth";
        }
        return null;
    }

    public void dispose() throws SaslException {
    }
}

The custom SASL SAMPLE mechanism defined by the SampleServer class, shown above, extends the SASL interface SaslServer. It simply calls the standard SaslServer methods one by one, to perform the following security operations:


Example 11–12 Custom SASL Server

public class SampleClient implements SaslClient {

    private byte[] username;
    private boolean completed = false;

    public SampleClient(String authorizationID) throws SaslException {
        if (authorizationID != null) {
            try {
                username = ((String)authorizationID).getBytes("UTF8");
            } catch (UnsupportedEncodingException e) {
                throw new SaslException("Cannot convert " + authorizationID
                                        + " into UTF-8", e);
            }
        } else {
            username = new byte[0];
        }
    }

    public String getMechanismName() {
        return "SAMPLE";
    }

    public boolean hasInitialResponse() {
        return true;
    }

    public byte[] evaluateChallenge(byte[] challengeData) 
            throws SaslException {
        if (completed) {
            throw new SaslException("Already completed");
        }
        completed = true;
        return username;
    }

    public boolean isComplete() {
        return completed;
    }

    public byte[] unwrap(byte[] incoming, int offset, int len) 
        throws SaslException {
        throw new SaslException("No negotiated security layer");
    }

    public byte[] wrap(byte[] outgoing, int offset, int len)
        throws SaslException {
        throw new SaslException("No negotiated security layer");
    }

    public Object getNegotiatedProperty(String propName) {
        if (propName.equals(Sasl.QOP)) {
            return "auth";
        }
        return null;
    }

    public void dispose() throws SaslException {
    }
}

The SampleClient class is an extension of the SaslClient interface, and calls the standard SaslClient methods one by one, to perform the following operations that are expected by the SampleServer:

With these SASL server and client mechanisms defined, they can then be instantiated by their respective factories.


Example 11–13 A Custom SASL Server Factory

public class ServerFactory implements SaslServerFactory {
   
     public SaslServer createSaslServer(String mechs,
				       String protocol,
				       String serverName,
				       Map props,
				       CallbackHandler cbh)
          throws SaslException {
	         if (mechs.equals("SAMPLE")) {
	             return new SampleServer();
	         }
	         return null;
     }

    public String[] getMechanismNames(Map props) {
	        return new String[]{"SAMPLE"};
    }
}

This basic implementation of the SaslServerFactory interface creates SampleServer instances when it is called with the SASL mechanism parameter set to SAMPLE. None of the other parameters are used by this mechanism.

The SampleClientFactory creates SampleClient instances in exactly the same way as the SampleServerFactory creates SampleServer instances.


Example 11–14 SASL SAMPLE Provider Class

public final class Provider extends java.security.Provider {
    public Provider() {
	super("SampleSasl", 1.0, "SAMPLE SASL MECHANISM PROVIDER");
	put("SaslClientFactory.SAMPLE", "ClientFactory");
	put("SaslServerFactory.SAMPLE", "ServerFactory");
    }
}

The SASL SAMPLE Provider constructor shown above specifies the name of the provider (SampleSasl), the version of this provider (in this case, 1.0) and a description of the provider. It then defines the server and client factories that are used to create SASL servers and clients implementing this SASL mechanism.

With the mechanisms above thus defined, all the Server and Client classes used in this example require the correct Provider to be installed in the environment.


Example 11–15 Adding a Provider to a JMX Connector Server

public class Server {

    public static void main(String[] args) {
        try {
            MBeanServer mbs = MBeanServerFactory.createMBeanServer();
            HashMap env = new HashMap();

            Security.addProvider(new Provider());
            env.put("jmx.remote.profiles", "SASL/SAMPLE");
            env.put("jmx.remote.x.access.file",
      		    "config" + File.separator + "access.properties");

            JMXServiceURL url = new JMXServiceURL("jmxmp", null, 5555);
            JMXConnectorServer cs =
                JMXConnectorServerFactory.newJMXConnectorServer(url, 
                                                                env, 
                                                                mbs);

            cs.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

The SAMPLE SASL mechanism is passed into the Client in the same way.

To Run the Secure JMXMP Connector Example with a SASL Provider

Run this example from within the examplesDir/current/Security/jmxmp/sasl_provider directory.

  1. Compile the Java classes.


    $ javac -classpath classpath \
          mbeans/SimpleStandard.java \
          mbeans/SimpleStandardMBean.java \
          server/Server.java \
          client/Client.java \
          client/ClientListener.java \
          sample/Provider.java \
          sample/ClientFactory.java \
          sample/ServerFactory.java \
          sample/SampleClient.java \
          sample/SampleServer.java 
    
  2. Start the Server.


    $ java -classpath server:sample:mbeans:classpath Server & 
    

    You will see confirmation of the creation of the MBean server, the initialization of the environment map and the launching of the JMXMP connector and its registration in the MBean server.

  3. Start the Client.


    $ java -classpath client:sample:mbeans:classpath Client 
    

    You will see confirmation of the creation of the JMXMP connector client, the initialization of the environment map, the connection to the MBean server and the performance of the various MBean operations followed by the closure of the connection.