Perform Java Linux (Ubuntu) Kerberos SSO (Single Sign On)

Finally after almost 8 months of efforts on trying to find a solution to this problem I was able to successfully resolve it. According to me this is a bug in Java / JVM implementation (https://forums.oracle.com/forums/thread.jspa?messageID=6384573 will tell you more about it).

People have been frustrated about it, sweated about it but so far there wasn't a solution other than trying to classify this as a bug or trying some distinctly useless routes that resolved in nothing more than additional errors. Now let's get to the case:

GOAL:
Create a Java application (/service but services should use keytab) that is capable of performing kerberos Single-Sign-On (SSO) in Linux environment using JAAS. Just to make a few points clear about the premise here:

  • Using Java application: command line/desktop but NOT talking about HTTP TGT delegation (SPNEGO)
  • Using JAAS based Kerberos Single-Sign-On
  • Using the famous Krb5LoginModule
  • Against Active Directory server acting as Kerberos Authentication Server / Master 
  • With kerberized authentication for machine (client) accounts (libpam-krb5)

In this case Ubuntu is my target OS that is running this program. Windows Server 2008 R2 based Active Directory Server is used as the Kerberos Server.

PROBLEM:
The following code was being used:
jaas config file: (login.conf)
SampleServer {
     com.sun.security.auth.module.Krb5LoginModule
         required doNotPrompt=true renewTGT=true debug=true
};

Executing Code:
public static void main(String[] args) throws Exception{
     System.setProperty("java.security.krb5.realm", "TEST.ORG");
     System.setProperty("java.security.krb5.kdc", "dc.test.org");        
     System.setProperty("java.security.auth.login.config","login.conf");
     LoginContext ctx=new LoginContext("SampleServer", null);
     ctx.login();
     
}

When you try to perform JAAS based authentication then you get the following error:

Exception: 
javax.security.auth.login.LoginException: Unable to obtain Princpal Name for authentication

Debug is  true storeKey false useTicketCache true useKeyTab false doNotPrompt true ticketCache is null isInitiator true KeyTab is null refreshKrb5Config is false principal is null tryFirstPass is false useFirstPass is false storePass is false clearPass is false
Acquire TGT from Cache
Principal is null
null credentials from Ticket Cache
[Krb5LoginModule] authentication failed 
Unable to obtain Princpal Name for authentication 

SOLUTION:
After about 8 months of periodic research I finally figured this out. After making various attempts and fiddling with encryption settings as suggested by many other people as well as trying to understand the Krb5LoginModule code no outcome was reached. A few minutes ago I stumbled on trying to change a config parameter for Krb5LoginModule the ticketCache  parameter which can be set like 

SampleServer {
     com.sun.security.auth.module.Krb5LoginModule required 
useKeyTab=true 
doNotPrompt=true 
renewTGT=true 
debug=true
ticketCache="/tmp/krb5cc_1001_..."
};

Now the thing is if you run a klist command on your terminal you will see the precise ocation of the latest krb5 TGT file (this file is dynamic and keeps changing everytime a kerberos ticket is obtained for the account) but the filename is preserved via the KRB5CCNAME environment variable. The bug with Java's implementation is for some reach this variable is not read and if you look at my error dump from debugging you will see that ticketCache is null. So I was hoping if I could actually set this variable dynamically I will have resolved the error since. When you specify the full path of the TGT file the JAAS based program is able to fetch your ticket and use it for SSO purpose. 

After digging some more I stumbled upon how JAAS actually sets configuration for the login modules. It uses the Configuration class for this.

Reading the javadoc has the following line: 
This Configuration specifies that an application named, "Login", requires users to first authenticate to the com.sun.security.auth.module.UnixLoginModule, which is required to succeed. Even if the UnixLoginModule authentication fails, thecom.sun.security.auth.module.Krb5LoginModule still gets invoked. This helps hide the source of failure. Since the Krb5LoginModule is Optional, the overall authentication succeeds only if the UnixLoginModule (Required) succeeds.
Also note that the LoginModule-specific options, useTicketCache="true" and ticketCache=${user.home}${/}tickets", are passed to the Krb5LoginModule. These options instruct the Krb5LoginModule to use the ticket cache at the specified location. The system properties, user.home and / (file.separator), are expanded to their respective values.

What this told me is that jaas config files can take in variables!!!!!

Now I tried to set ticketCache="${KRB5CCNAME}" but this did not work apparently it's true that jaas config files can take variables but those are JVM system property variables NOT system environment variables.

All I needed to do now was copy the value of $KRB5CCNAME from system environment variables to system properties and voila PROBLEM SOLVED

So here's the code that actually works:

jaas config file: (login.conf)
SampleServer {
     com.sun.security.auth.module.Krb5LoginModule
         required 
doNotPrompt=true 
renewTGT=true 
debug=true
ticketCache="${KRB5CCNAME}"
};

Executing Code:
public static void main(String[] args) throws Exception{
     System.setProperty("KRB5CCNAME", System.getenv("KRB5CCNAME"));
     System.setProperty("java.security.krb5.realm", "TEST.ORG");
     System.setProperty("java.security.krb5.kdc", "dc.test.org");        
     System.setProperty("java.security.auth.login.config","login.conf");
     LoginContext ctx=new LoginContext("SampleServer", null);
     ctx.login();
     
}


Comments