Previous | Next | Trail Map | Tips for LDAP Users | Security

GSS-API/Kerberos v5 Authentication


Note: The LDAP provider's GSS-API implementation uses the Java Bindings for GSS-API (RFC 2853) for GSS-API/Kerberos v5 support. If you are using the Java 2 SDK, v1.4, then the Java GSS and Kerberos implementations are already included so you need to take no further action. Otherwise, you need to install a Java GSS and Kerberos implementation in order for the examples in this section to work.
GSS-API is Generic Security Service API (RFC 2744). It provides a common interface for accessing different security services. One of the most popular security services available for GSS-API is the Kerberos v5 (see RFC 1510 and RFC 1964). Kerberos v5 is the security system used in Microsoft's Windows 2000 platform.

The GSS-API SASL mechanism is described in RFC 2222. It specifies how GSS-API services can be used for SASL authentication and establishment of a security layer. The GSS-API SASL mechanism was originally intended to support any GSS-API implementation, not just Kerberos v5. However, some problems related to the secure negotiation of a GSS-API mechanism beneath the SASL layer were identified and the GSS-API SASL mechanism was retrofitted to mean only Kerberos v5. The GSS-API SASL mechanism is supported only by LDAP v3 servers. The LDAP servers that support the GSS-API SASL mechanism include Windows 2000's Active Directory server, OpenLDAP, and the SunONE Directory Server v5.2.

The use of the GSS-API SASL authentication mechanism requires a slightly different programming model than the use of the other SASL mechanisms that have been shown previously. This is because the GSS-API/Kerberos subsystem is a security system for the entire Java platform, whereas SASL mechanisms such as Digest-MD5 and CRAM-MD5 are independent authentication mechanisms. In other words, the GSS-API/Kerberos subsystem allows a Java application to authenticate to Kerberos once, and then use the acquired security credentials to access a whole array of services securely, including directory services. Mechanisms such as Digest-MD5 and CRAM-MD5 provide security only for a single LDAP session with an LDAP server.

To use the GSS-API SASL mechanism, you must do the following.

  1. Authenticate to Kerberos.
  2. Assume the identity of the authenticated principal.
  3. When creating the initial context, set the Context.SECURITY_AUTHENTICATION(in the API reference documentation) environment property to the string "GSSAPI".
In a standalone application, you would have to perform all three steps. However, in an applet environment or other container environments, the first two steps might be performed for you already if you are accessing a directory service that uses the same Kerberos service as the container. This section shows how to perform all three steps.

Authenticating to Kerberos

On the Java platform, you use the Java Authentication and Authorization Service (JAAS) for authentication to security services. We give enough details in this section to show how to use the JAAS to authenticate to Kerberos. For additional information and examples, see the JAAS and Java GSS-API Tutorials.

A Java application logs into one or more security systems by first creating a LoginContext(in the API reference documentation). You supply to the LoginContext constructor(in the API reference documentation) the name of the class that is doing the authentication and a CallbackHandler(in the API reference documentation). Here is code that creates a LoginContext.

LoginContext lc = new LoginContext(GssExample.class.getName(), new SampleCallbackHandler());
The class name is used by the JAAS to identify the login modules that are required for the class. The JAAS allows dynamic configuration of login modules. When the program is run, you specify a JAAS configuration file that specifies the login modules to use. Here is a configuration file that requests a Kerberos v5 authentication module for the our sample program named GssExample.
GssExample {
  com.sun.security.auth.module.Krb5LoginModule required client=TRUE;
};

The CallbackHandler is used by the login modules to obtain the required authentication information. The Kerberos v5 login module, for example, requires a username and password. In the example, the callback handler is SampleCallbackHandler, the same handler we used for the CRAM-MD5 callback example.

After you have create an instance of LoginContext, you perform the authentication by invoking LoginContext.login()(in the API reference documentation). This method throws a subclass of LoginException if the authentication fails. The exception indicates the cause of the failure. For certain exceptions, such as FailedLoginException(in the API reference documentation), you might want to reattempt the authentication by reinvoking login() in case the failure was due to a mistyped password or username.

Here is an example that performs the authentication without any retries.

// 1. Log in (to Kerberos)
LoginContext lc = null;
try {
    lc = new LoginContext(GssExample.class.getName(),
	new SampleCallbackHandler());

    // Attempt authentication
    // You might want to do this in a "for" loop to give
    // user more than one chance to enter correct username/password
    lc.login();

} catch (LoginException le) {
    System.err.println("Authentication attempt failed" + le);
    System.exit(-1);
}

Assuming the Identity of the Authenticated Principal

To act on behalf of the authenticated principal, use one of the following methods. The Subject(in the API reference documentation) argument is obtained by invoking LoginContext.getSubject()(in the API reference documentation) on the LoginContext that was used for Kerberos authentication. PrivilegedAction(in the API reference documentation) and PrivilegedExceptionAction(in the API reference documentation) define the computation to be performed with the privileges of the authenticated user enabled. PrivilegedAction and PrivilegedExceptionAction are used for computations that, respectively, do and do not throw checked exceptions. The AccessControlContext(in the API reference documentation) argument is used to specify a security context to use other than that of the caller. For example, a worker thread might use the security context of its master instead of its own. For more information about security contexts, PrivilegedAction and PrivilegedExceptionAction, see the Java 2 Platform Security Architecture.

Continuing with our example, we need to define a class that implements either PrivilegedAction or PrivilegedExceptionAction and encapsulate the JNDI calls within the class's run() method. In our example, we defined a class called JndiAction that performs the directory access calls using the JNDI. (The definition of JndiAction is described in the next section.) The following code executes JndiAction's run() method with the privileges of the authenticated Kerberos principal enabled.

Subject.doAs(lc.getSubject(), new JndiAction(args));

Performing the JNDI Task

From within the PrivilegedAction.run()(in the API reference documentation) and PrivilegedExceptionAction.run()(in the API reference documentation)methods, the JNDI calls are made in the same way as described in the rest of this book. Remember to make the calls in the class's run() method (not its constructor). The only thing to note is that when you specify the hostname in the URL in the Context.PROVIDER_URL(in the API reference documentation) property, be sure to use the name that the host is known as in the Kerberos database. Typically, this means using the fully qualified hostname. If you use a name that is not in the Kerberos database, the program will fail with an authentication error.

The following code shows how to set the environment properties for performing authentication using the GSS-API SASL mechanism.

env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

// Must use fully qualified hostname
env.put(Context.PROVIDER_URL, "ldap://ldap.jnditutorial.org:389/o=JndiTutorial");

// Request the use of the "GSSAPI" SASL mechanism
// Authenticate by using already established Kerberos credentials
env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");

Here is the complete definition of JndiAction.

class JndiAction implements java.security.PrivilegedAction {
    private String[] args;

    public JndiAction(String[] origArgs) {
	this.args = (String[])origArgs.clone();
    }

    public Object run() {
	performJndiOperation(args);
	return null;
    }

    private static void performJndiOperation(String[] args) {

	// Set up environment for creating initial context
	Hashtable env = new Hashtable(11);

	env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

	// Must use fully qualified hostname
	env.put(Context.PROVIDER_URL, "ldap://ldap.jnditutorial.org:389/o=JndiTutorial");
    
	// Request the use of the "GSSAPI" SASL mechanism
	// Authenticate by using already established Kerberos credentials
	env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");

	try {
	    /* Create initial context */
	    DirContext ctx = new InitialDirContext(env);

	    // do something useful with ctx
		...

	    // Close the context when we're done
	    ctx.close();
	} catch (NamingException e) {
	    e.printStackTrace();
	}
    }
}

Running the Example

Before running the example, ensure that the Kerberos configuration that you are using is for the Kerberos system that is also being used by your target directory server. Kerberos configuration differs on different operating system platforms. For our example, we use a configuration file supplied in the command line. To run the example in your environment, you must change at a minimum the realm declaration and the hostname of the Kerberos server (i.e., the Kerberos Key Distribution Center, or Kerberos KDC). Suppose we are using the Kerberos realm JNDITUTORIAL.ORG and the Kerberos server kerberos.jnditutorial.org. The first few lines of the configuration file would look as follows.

[libdefaults]
        default_realm = JNDITUTORIAL.ORG
	default_checksum = rsa-md5

[realms]
        JNDITUTORIAL.ORG = {
                kdc = kerberos.jnditutorial.org
        }

[domain_realm]
	.jnditutorial.org = JNDITUTORIAL.ORG
You might need to change the default_checksum setting and some other settings in the file depending on the ciphers and features supported by the Kerberos server.

Here is the command line for running the program using this configuration file and the JAAS configuration file shown previously. This assumes the use of the Java 2 SDK, v1.4.

# java \
    -Djava.security.auth.login.config=gsseg_jaas.conf \
    -Djava.security.krb5.conf=krb5.conf \
    GssExample
When you run this program, it will prompt you for the username and password of the Kerberos principal being authenticating.

Permissions

If you run this example with a security manager installed, or if you adapt this example to work in an applet, then you need to grant the program or applet permission to connect to the Kerberos server and to initiate service requests with both the Kerberos and LDAP servers. These permissions are in addition to the permissions you need to connect to the LDAP server. The application, or the applet if it will be performing the Kerberos authentication, also needs permission to create a LoginContext and to invoke the doAs() method. Here are the permissions used for our example. For your own code, replace GssExample with the class name of your program, JNDITUTORIAL.ORG with your Kerberos realm name, and ldap.jnditutorial.org and kerberos.jnditutorial.org with the hostnames of the LDAP and Kerberos servers, respectively. Also, insert the codebase of the program in the grant statement.


permission java.net.SocketPermission "ldap.jnditutorial.org", "connect";
permission java.net.SocketPermission "kerberos.jnditutorial.org", "connect";

permission javax.security.auth.AuthPermission "createLoginContext.GssExample";
permission javax.security.auth.AuthPermission "doAs";	

permission javax.security.auth.kerberos.ServicePermission "krbtgt/JNDITUTORIAL.ORG@JNDITUTORIAL.ORG", "initiate";
permission javax.security.auth.kerberos.ServicePermission "ldap/ldap.jnditutorial.org@JNDITUTORIAL.ORG", "initiate";
To run the example with a security manager installed, enter the following. The krb5.policy file is the security policy file that contains the permissions.
# java \
    -Djava.security.auth.login.config=gsseg_jaas.conf \
    -Djava.security.krb5.conf=krb5.conf \
    -Djava.security.manager \
    -Djava.security.policy=krb5.policy \
    GssExample

Setting Additional Parameters

You may request integrity and privacy protection by using the "javax.security.sasl.qop" environment property. Cipher selection is configured via the Kerberos configuration mechanism (such as the configuration file); the "javax.security.sasl.strength" environment property is ignored.

When requesting integrity or privacy protection, you may specify the maximum receive buffer size to use by using the "javax.security.sasl.maxbuffer" environment property. If you do not specify a maximum, then an implementation-dependent default is used.

When requesting to use an authorization id different from the authentication id, use the "java.naming.security.sasl.authorizationId" environment property.


Note: The Active Directory in Windows 2000 ignores the maximum receive buffer size and the authorization id.

To request mutual authentication, set the "javax.security.sasl.server.authentication" environment property to "true". The following code shows how to set the environment properties for performing mutual authentication using the GSS-API SASL mechanism.

env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

// Must use fully qualified hostname
env.put(Context.PROVIDER_URL, "ldap://ldap.jnditutorial.org:389/o=JndiTutorial");

// Request the use of the "GSSAPI" SASL mechanism
// Authenticate by using already established Kerberos credentials
env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");

// Request mutual authentication
env.put("javax.security.sasl.server.authentication", "true");


Previous | Next | Trail Map | Tips for LDAP Users | Security