| 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 and OpenLDAP.

> 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` 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`. You supply to the `LoginContext` constructor the name of the class that is doing the authentication and a `CallbackHandler`. 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()`. 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`, 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.
> - `Subject.doAs(Subject, PrivilegedAction)`
> - `Subject.doAs(Subject, PrivilegedExceptionAction)`
> - `Subject.doAsPrivileged(Subject, PrivilegedAction, AccessControlContext)`
> - `Subject.doAsPrivileged(Subject, PrivilegedExceptionAction, AccessControlContext)`
> The `Subject` argument is obtained by invoking `LoginContext.getSubject()` on the `LoginContext` that was used for Kerberos authentication. `PrivilegedAction` and `PrivilegedExceptionAction` 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` 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()` and `PrivilegedExceptionAction.run()`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` 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");

| Security