A Java SE application or a Tomcat server runs under a native operating system userid. When it uses files and other local resources, it does so under the authority of that local system login. However, when an SE application uses a network service (say a database through JDBC), it has to provide a separate userid/password login to the remote server. A Web application running under Tomcat has the reverse problem, obtaining the identity of the remote user in order to access a personal profile or make access control decisionsThere are three ways to authenticate a Netid and Password to Active Directory.
- JAAS configured to validate the password using Kerberos 5 logon protocol
- JAAS configured to use LDAP logon.
- A direct LDAP logon from CAS.
The general CAS community has agreed that the third option is best. The cas-server-ldap module to go directly to LDAP (in Yale's case through the LDAP forwarding VIP on the F5) also provides us with the option to test the "last password change" timestamp (for password expiration) and to fetch additional AD properties about the user (which we do not use but is essential for CAS 3+ SAML support that other schools use). However, we did use JAAS at one time and it is a complicated enough feature of Java that the documentation below is still useful for other purposes.
A Java Web application runs on a machine under the userid that starts the server (typically "tomcat"). Java programs typically do not have any other credentials.
However, JAAS provides Java programs with a generic interface to various different network based "login" protocols. Through JAAS a Java program can present a userid and password (or a Kerberos keytab file) and login to Kerberos or an LDAP server. Other than validating the userid and password, it is unclear what else you get from this. For example, JDBC still requires you to present the userid and password separately on the database Connection, and JNDI still requires you to present the userid and password when setting up the InitialContext.
The one useful feature of JAAS is that it allows a Java program to obtain a Ticket Granting Ticket from Kerberos, and using it to obtain and validate Service Tickets. This is a mostly undocumented feature, however, and it is fairly technical and requires a lot of additional setup.
So in practice JAAS is an API that is not really "connected to anything". You can use it to login, but then you can't do anything with the login. However, if in the future anyone developed a reason to use a login API, one would already exist.
CAS can use JAAS to validate the userid and password to a Kerberos KDC or an LDAP server. AD supports both of these protocols. Currently we use LDAP, but we call it through JNDI rather than JAAS.
In the standard Java Runtime from Sun, there are JAAS plug-in LoginModule classes for Kerberos 5, LDAP, and several legacy network protocols (NT Lan Manager, NIS) that are probably no longer of current interest. However, given the standard interface exposed by JAAS, you can obtain software from third party vendors that either provide better versions of these protocols or new protocols for servers or network appliances.
To use JAAS, Java code creates a LoginContext associated with a bundle of configuration parameters that names one or more plug-in service providers. Each provider tries to validate the credentials against a network service. Success generates Subject and Principal objects chained to the LoginContext.
- The LdapLoginModule accepts a userid/password, uses them to login to an LDAP directory, and may return information from the LDAP User object in the generated Subject and Principal objects. LDAP attributes on the User object could be converted into Roles to make access control decisions, but in many cases this will require a customized version of the LoginModule that knows what attributes to fetch and how to generate the Roles.
- The Krb5LoginModule accepts a userid/password or a keytab file containing the secret key. It returns Subject and Principal objects containing the secret key and the Ticket Granting Ticket. The TGT can be used to gain access to other network services that use Kerberos.
- The Krb5LoginModule can alternately accept a Service Ticket and return the userid of the remote user who sent it in the Subject/Principal.
- The CasLoginModule (yes one has been written for JBoss) accepts the CAS Service Ticket, validates it to the CAS server, and returns the netid and Proxy info in the Subject/Principal objects. Through Proxy services, this identity can then be propagated to another JBoss server when a request is made to a remote JBoss EJB.
Because JAAS exposes a single published service provider interface, vendors of specialized identity hardware can write their own plug-in XxxLoginModule class and distribute smart cards, USB devices, or other forms of credential. Exotic credentials (more than userid and password) may require some customization in the user login interface of the application, but JAAS provides a standard language-wide interface.
Shared Authentication
CAS is a Single Sign-On mechanism. The user logs on once to the CAS server and identifies himself though that one server to all the applications on the network. However, CAS can only be used by Web based applications that can be adapted to use its protocol. When you cannot use SSO, then you still want to avoid ending up with a dozen different systems each with its own set of userids and passwords.
...
Shared authentication becomes more complicated as authentication itself becomes more involved. In the old days you just used passwords and that was good enough. In the new world it may be necessary to establish identity by using passwords and some physical device that plugs into a USB port. Pluggable authentication has to be able to extend to these more complicated environments.
JAAS - Pluggable Java Authentication
JAAS became the Java SE version of the PAM idea. Standard Java releases since 1.4 have included the javax.security.auth.* packages. They work like JDBC in exposing a single standard front set of classes that are extended by adding external libraries to the classpath that provide additional classes implementing a service provider interface. JDBC is extended by libraries of database drivers, while JAAS is extended by login providers.Out of the box, Sun provides classes to do the Kerberos 5 and LDAP login function. Those are probably the best choices for any institution planning to use standard internet protocols and network based software solutions. However, hardware manufactures selling USB dongles and Smart Cards may also sell network appliances that authenticate users of these hardware devices. JAAS provides a standard interface that can be used for even exotic requirements. The vendor simply provides a JAR file with classes that implement the JAAS service provider interface, and then the system administrator can configure the vendor authentication as easily as one of the standard Sun classesLoginModule classes.
The application program or container presents the name of a bundle of parameters. JAAS looks up the name in its configuration files, and the named bundle specifies the fully qualified name of one or more classes implementing the LoginModule Service Provider Interface. The application program or container provides credentials and JAAS passes them on to the configured list of LoginModules. Obviously the named class must be found somewhere in the Classpath, but whether it was added to the JRE, provided by the container, or bundled in the application is a local decision.
The first time someone learns about JAAS, they probably use it to validate a userid and password against a standard network server (like Microsoft AD). At first, the API seems more complicated than you expect:
...
The reason for the callback class is that userid and password are not the only credentials someone can use to prove their identity. In addition to password, a Kerberos 5 KDC can authenticate using a keytab file containing the secret private key derived from the password. Keytab files are a common way to identify not people, but specific services residing on machines in the network. When the private key is stored on a USB device, then the callback provides an opportunity for a special Java driver to use the device to prove identity.
...
There are also some extra features to JAAS, particularly when it is used in Kerberos 5. However, before these features can be understood, we need to probe a bit more into the parts of the JAAS API that people do not learn when they are just using it to verify a password.
JAAS Login Configuration
A JDBC database driver requires no special configuration, but then the client application program has to supply the URL to the server and the connection parameters. JAAS is designed to remove the entire authentication process from the application and move it to the system administrator. As a result, the JAAS "login configuration" looks something like the configuration of a Spring Bean or a Servlet Filter.
...
This configuration bundle selects the standard Sun LDAP plug-in module supplied with every JRE. It provides a URL with server name and starting search location, and an LDAP query that can be used to locate an LDAP User object given a Netid argument (represented by the {USERNAME} variable.) With this configuration, JAAS will go to a Domain Controller in the Yale Active Directory, search for a legacy userid matching the userid supplied by the JAAS client application, and then validate the password against the unique object returned from the search. Using a search gets around the problem when some departments create userids in their own OU instead of using the common Users container. The bundle has been given the name "myauth". A client program can pass this name to JAAS to select this particular configuration and authenticate using the Yale AD.
...
Code Block |
---|
<application-policy name="myauth"> <authentication> <login-module code="com.sun.security.auth.module.LdapLoginModule" flag="required"> <module-option name="userProvider">ldaps://yu.yale.edu/dc=yu,dc=yale,dc=edu</module-option> <module-option name="userFilter">(&(sAMAccountName=\{USERNAME\}))</module-option> <module-option name="useSSL">true</module-option> </login-module> </authentication> </application-policy> |
The third possibility is that the individual application may have a JAAS configuration file embedded in it. A path to a login configuration file is part of the client API. Of course, this packaging violates the original idea of separating authentication as a system administration function from the application programmer's domain, but not all institutions have Java literate system administrators.
Interestingly, when the client specifies a login file it is added to, instead of replacing, the login configuration files supplied by the JRE. A configuration parameter bundle name must be unique within the complete set of files used (an exception is thrown if the same name is found in both the application file and the JRE file). Thus the application cannot override a bundle name already defined by the JRE but can only add new bundle names.
...
is that the individual application may have a JAAS configuration file embedded in it. The path to the login configuration file is a Java system environment variable that can be specified by the application before it calls JAAS.
Now for the important point. Login configuration files are merged, they don't replace each other. Furthermore, there is no rule that allows a named bundle of parameters to be overridden by another source. The name the application or container is looking for ("myauth" in these examples) must be found in only one configuration file or JAAS will simply throw an exception.
Name the Service or the Application?
JAAS configuration offers enormous flexibility, but this leaves an ambiguity that should be resolved by institutional policy. If it is not given some global thought, you leave room for inconsistency and confusion.
...
Alternately, an institution could specify a small number of bundle names representing the generic type of authentication (an authentication design pattern) that an application might select (SingleSignOn, SingleSignOnWithProxy, SharedAuthentication). Note that these names do not reflect a particular protocol or server, but neither are they limited to a particular application. Unfortunately, these design patterns have not be assigned industry standard names, so this preferred approach will probably vary from institution to institution.
The JAAS Subject and Principal Objects
An application using JAAS creates a LoginContext object associated with the name of a bundle of configuration data. Up to this point each example has shown the configuration of a single fully qualified classname and its parameters. However, JAAS allows a named bundle to configure more than one plug-in module, allowing the user to make a single call and log into either the first system that accepts him or more than one back end system (Kerberos and LDAP for example).
The JAAS client application calls the login() method of the LoginContext object. After a sucessful login, the LoginContext has acquired a javax.security.auth.Subject object and one or more javax.security.auth.Principal objects (there will be one Principal for every sucessful login to a different plug-in LoginModule class in the named configuration bundle).
...
Actually examining the generated objects may provide a bit of interesting but probably useless information. For example, after a successful LDAP login to Active Directory by userid "gilbert" (which in the previous configuration example above searches for an LDAP User object with sAMAccountName=gilbert), one might be mildly interested that the Distinguished Name of the LDAP User object was interesting but probably useless information. For example, if the parameter bundle name tells JAAS to use both the Krb5LoginModule and the LdapLoginModule, then two successful logins of the name "gilbert" will generate two Principal objects with ID names "gilbert@YU.YALE.EDU" (Krb5 with the realm name appended) and "CN=gilbert, CN=Users, DC=yu, DC=yale, DC=edu" , but this is hardly an unexpected result(LDAP to the Active Directory).
The JAAS Objects when you use Krb5LoginModule
JAAS internal behavior becomes a lot more interesting in the special case where you use the standard Sun Kerberos 5 plug-in module. Extra function becomes available and a variety of new use cases is exposed when you add a few extra configuration parameter options.
The first interesting parameter is "useTicketCache=true". This exploits the special permission that JAAS has to do different things on different operating systems. With this parameter, JAAS shares whatever is the native host operating system Kerberos login ticket storage mechanism. On a Windows machine logged into an Active Directory domain, JAAS can simply connect to the existing login and create a Subject and Principal that reflects the host environment native Windows userid.
SPN
Up to this point, we have discussed the userid of individuals. However, in Kerberos each server has an identity called the Service Principal Name. A Web Server on a machine named "secure.its.yale.edu" has an SPN of "HTTP/secure.its.yale.edu". When you use real Kerberos protocol, including real K5 Service Tickets, then the client logs on to Kerberos with his userid, the server logs on with the SPN, and then the client obtains a Service Ticket that the server can validate.
...
In the special case where Active Directory provides the Kerberos service, Microsoft allows a standard SPN to be "mapped" to a dummy User object also in AD. For example, one can create a dummy user named "s_cas" in Active Directory and give it a normal password. Then, one can use administrator tools in AD to map the "HTTPsetspn.exe to map the "HTTP/secure.its.yale.edu" SPN ID to the "s_cas" userid. Now instead of using the keytab=... form of login, the service (corresponding to URL "https://secure.its.yale.edu" SPN ID to the /cas") can login using the standard userid/password API of JAAS with the userid "s_cas" userid. Now instead of using the keytab=... form of login, the service (corresponding to URL "https://secure.its.yale.edu/cas") can login using the standard userid/password API of JAAS with the userid "s_cas" and the password assigned to that dummy user account.
Either way, after the service logs in through the Krb5LoginModule of JAAS, if it has requested that the ticket be stored in a ticket cache and uses the "storeKey=true" configuration parameter, the Subject and Principal objects created by the LoginContext contain the information that the Java service needs to validate real Kerberos 5 Service Tickets.
Real Service Ticket Validation
Which brings us to yet another use of the Krb5LoginModule. In addition to logging real desktop user clients into Kerberos with a userid and password, and logging a service SPN into Kerberos using a keytab or mapped userid, Krb5LoginModule can "login" a the remote network client using the Service Ticket supplied by that remote client specifically for use by this particular service SPN.
Obviously somewhere under the covers there is an explicit description of this specific use of the JAAS API. However, in most common cases it is used under the covers by some other technology or library. The first step is to log in the service (using the keytab or mapped service userid). Then from the credentials stored in the Subject and Principal objects the code can extract the private key of the service which is used by Service Ticket validation.
Now you need an entirely different LoginContext (because now the Subject will be the remote use of the service and not the service itself). It too has to be configured to use Krb5LoginModule and obviously it will be using the same KDC as the previous step. However, it will have different configuration parameters and therefore will probably use a different bundle name. Now we waive our hands a bit because the details are uncertain, but somehow the remote user obtains a Service Ticket targeted to this SPN and it transmits the ticket to us. Using the Ticket instead of a userid/password as credentials, and plugging into the second LoginContext the private key obtained from the Subject object of the first LoginContext we provide Krb5LoginModule everything it needs to validate the Service Ticket and create in the second LoginContext Subject and Principal objects that represent the authenticated remote user.
Now the gotcha. There is no public standard Java API that directly obtains Service Tickets for a remote service. However, GSSAPI does this under the covers as part of its protocol. When it does this, GSSAPI and JAAS operate together sharing Kerberos configuration info and some ticket and key objects. It is through this collaboration and not through some pure, naked Kerberos API that we most commonly find Kerberos Service Ticket validation operating. In Web applications, this means the use of SPNEGO authentication.
JAAS in a Container (JBoss)
The JAAS client program API and the Service Provider Interface (SPI) to the plug-in modules represent two perfectly acceptable interfaces that anyone trying to solve the same problem would have to reinvent. Furthermore, the particular Sun plug in modules provide sophisticated services (especially Krb5LoginModule) that it would be wasteful to try and reproduce.
However, the standard JAAS service is a J2SE interface. The application creates a LoginContext, logs in, and obtains Subject and Principal objects. Only the application knows it has done this. Furthermore, the LoginContext and Subject are private to the class that does the login and have no wider visibility unless the application code chooses to invent some convention.
JBoss exposes to its JVM environment what might be called JAAS+. It is the standard JAAS service, plug-ins, and an XML version of the standard configuration file. The extra added feature is that the JAAS generated Subject and Principal objects are not only returned to the caller's LoginContext, but they also become part of the container's Security Context environment. They become a feature of the Request and the Thread that has been assigned to process it. When an EJB is called, they propogate to the EJB environment, even across a Remote call to an EJB on another JBoss server.
For this to work, the Subject/Principal has to be in some sense "serializable". Obviously if you login to Krb5LoginModule the ticket you receive cannot itself be transmitted to another machine. If the login presented a userid/password, then these "primary credentials" can be stored in the Subject/Principal, serialized, transmitted to a second system, and then the second system can do a second primary login presenting the same userid/password to its own Krb5LoginModule.
It remains to be seen how the SPNEGO support now incubating in JBoss will accomplish the same remote propagation of credentials when JBoss never had primary credentials to transmit.
Who's on First?
The CAS Server is (or can be) configured to use JAAS to validate a userid/password typed into the Login form. At Yale, JAAS has been configured to authenticate against the MIT K5 KDC.
However, other applications regard CAS itself as an authentication mechanism. If these applications (like JBoss) expect to authenticate users through a JAAS call, then it is possible to supply them with a CAS plug-in that uses the JAAS Service Provider Interface (SPI) to validate credentials (the ticket id) against a configured back end system (the CAS Server).
The first step toward sanity is not to confuse these two distinct uses of JAAS by the CAS Server (against Kerberos) and by the CAS client (against the CAS Server itself).
The simplest version of a CAS JAAS plug-in module simply validates a service ticket. Imagine that the code has been written as a class named CasServiceLoginModule. Configure the fully qualified class name under some bundle name in the JAAS configuration files. We assume the plug-in takes two parameters, the URL of the /serviceValidate operation in the CAS server and the value that will passed to CAS as the service= parameter. The ticket= value will be presented to the module as a parameter. If the environment strongly expects a userid/password, assume the ticket id will be passed in the password field. The CasServiceLoginModule then connects to the ServiceValidate URL, authenticates the ticket, obtains the userid and any other information that CAS chooses to share, and stores this in the Subject and Principal objects generated.
A CASify Lombardi - Teamworks Application via Jaas exists for JBoss. It has the additional function of obtaining a Proxy Granting Ticket so the Subject and Principal objects in the JBoss security context can "serialize" and propagate to a second JBoss system when there is a remote EJB call. However, in the current state of CAS protocol, use of Proxy tickets requires a callback to a configured HTTPS port, and that cannot be done without some container-specific configuration and logicand the password assigned to that dummy user account.
Either way, after the service logs in through the Krb5LoginModule of JAAS, if it has requested that the ticket be stored in a ticket cache and uses the "storeKey=true" configuration parameter, the Subject and Principal objects created by the LoginContext contain the information that the Java service needs to validate real Kerberos 5 Service Tickets.
Real Service Ticket Validation
Which brings us to yet another use of the Krb5LoginModule. In addition to logging real desktop user clients into Kerberos with a userid and password, and logging a service SPN into Kerberos using a keytab or mapped userid, Krb5LoginModule can "login" a the remote network client using the Service Ticket supplied by that remote client specifically for use by this particular service SPN.
Obviously somewhere under the covers there is an explicit description of this specific use of the JAAS API. However, in most common cases it is used under the covers by some other technology or library. The first step is to log in the service (using the keytab or mapped service userid). Then from the credentials stored in the Subject and Principal objects the code can extract the private key of the service which is used by Service Ticket validation.
Now you need an entirely different LoginContext (because now the Subject will be the remote use of the service and not the service itself). It too has to be configured to use Krb5LoginModule and obviously it will be using the same KDC as the previous step. However, it will have different configuration parameters and therefore will probably use a different bundle name. Now we waive our hands a bit because the details are uncertain, but somehow the remote user obtains a Service Ticket targeted to this SPN and it transmits the ticket to us. Using the Ticket instead of a userid/password as credentials, and plugging into the second LoginContext the private key obtained from the Subject object of the first LoginContext we provide Krb5LoginModule everything it needs to validate the Service Ticket and create in the second LoginContext Subject and Principal objects that represent the authenticated remote user.
Now the gotcha. There is no public standard Java API that directly obtains Service Tickets for a remote service. However, GSSAPI does this under the covers as part of its protocol. When it does this, GSSAPI and JAAS operate together sharing Kerberos configuration info and some ticket and key objects. It is through this collaboration and not through some pure, naked Kerberos API that we most commonly find Kerberos Service Ticket validation operating. In Web applications, this means the use of SPNEGO authentication.
JAAS in a Container (JBoss)
The JAAS client program API and the Service Provider Interface (SPI) to the plug-in modules represent two perfectly acceptable interfaces that anyone trying to solve the same problem would have to reinvent. Furthermore, the particular Sun plug in modules provide sophisticated services (especially Krb5LoginModule) that it would be wasteful to try and reproduce.
However, the standard JAAS service is a J2SE interface. The application creates a LoginContext, logs in, and obtains Subject and Principal objects. Only the application knows it has done this. Furthermore, the LoginContext and Subject are private to the class that does the login and have no wider visibility unless the application code chooses to invent some convention.
JBoss exposes to its JVM environment what might be called JAAS+. It is the standard JAAS service, plug-ins, and an XML version of the standard configuration file. The extra added feature is that the JAAS generated Subject and Principal objects are not only returned to the caller's LoginContext, but they also become part of the container's Security Context environment. They become a feature of the Request and the Thread that has been assigned to process it. When an EJB is called, they propogate to the EJB environment, even across a Remote call to an EJB on another JBoss server.
For this to work, the Subject/Principal has to be in some sense "serializable". Obviously if you login to Krb5LoginModule the ticket you receive cannot itself be transmitted to another machine. If the login presented a userid/password, then these "primary credentials" can be stored in the Subject/Principal, serialized, transmitted to a second system, and then the second system can do a second primary login presenting the same userid/password to its own Krb5LoginModule.
It remains to be seen how the SPNEGO support now incubating in JBoss will accomplish the same remote propagation of credentials when JBoss never had primary credentials to transmit.
Who's on First?
The CAS Server is (or can be) configured to use JAAS to validate a userid/password typed into the Login form. At Yale, JAAS has been configured to authenticate against the MIT K5 KDC.
However, other applications regard CAS itself as an authentication mechanism. If these applications (like JBoss) expect to authenticate users through a JAAS call, then it is possible to supply them with a CAS plug-in that uses the JAAS Service Provider Interface (SPI) to validate credentials (the ticket id) against a configured back end system (the CAS Server).
The first step toward sanity is not to confuse these two distinct uses of JAAS by the CAS Server (against Kerberos) and by the CAS client (against the CAS Server itself).
The simplest version of a CAS JAAS plug-in module simply validates a service ticket. Imagine that the code has been written as a class named CasServiceLoginModule. Configure the fully qualified class name under some bundle name in the JAAS configuration files. We assume the plug-in takes two parameters, the URL of the /serviceValidate operation in the CAS server and the value that will passed to CAS as the service= parameter. The ticket= value will be presented to the module as a parameter. If the environment strongly expects a userid/password, assume the ticket id will be passed in the password field. The CasServiceLoginModule then connects to the ServiceValidate URL, authenticates the ticket, obtains the userid and any other information that CAS chooses to share, and stores this in the Subject and Principal objects generated.
A CASify Lombardi - Teamworks Application via Jaas exists for JBoss. It has the additional function of obtaining a Proxy Granting Ticket so the Subject and Principal objects in the JBoss security context can "serialize" and propagate to a second JBoss system when there is a remote EJB call. The first JBoss authenticates the ticket as described previously, but it obtains a Proxy Granting Ticket and stores it in the Subject/Principal structure. Then when the JBoss Security Context asks the Subject to serialize itself (which only happens when you are making a remote EJB call), it obtains a Proxy Service Ticket for the remote system and sends its ID as content of the serialized Subject/Principal. On the receiving system, when these objects are de-serialized the code validates the Proxy Service Ticket and inserts the Netid in the secondary Subject/Principal.
There are a number of problems that make this code difficult to generalize. For one thing, the Proxy Callback requires that code be inserted into the Servlet layer of the container, and then the Ticket Validation and the Callback routine have to synchronize what they are doing. This is probably a system dependent solution. More importantly, you can only get a Proxy ticket if you know the service= of the receiving system, and that implies understanding the current system specific Security context and request processing context.Again, it will be interesting to see how SPNEGO handles this same problem if it gets integrated into JBoss, but that would not solve the problem for another container. Strictly speaking, the uncertainties here are not in the JAAS function but rather some missing features of the javax.security.auth.Subject interface.