title: Kerberos and LDAP: Why does Java 6 truncate LDAP host names when using Kerberos?
body: <p>My task is to connect to a Kerberized LDAP server through Java GSS API. I have a small sample app that I use to test my connection, my Kerberos configuration, connection parameters, etc, to be sure that I can connect to the LDAP server and retrieve data. The code for this is given below:</p>
  
  <pre><code>import java.io.FileInputStream;
  import java.io.IOException;
  import java.security.PrivilegedActionException;
  import java.security.PrivilegedExceptionAction;
  import java.util.HashMap;
  import java.util.Hashtable;
  import java.util.Map;
  import java.util.Properties;
  
  import javax.naming.Context;
  import javax.naming.NamingEnumeration;
  import javax.naming.NamingException;
  import javax.naming.directory.Attributes;
  import javax.naming.directory.DirContext;
  import javax.naming.directory.SearchControls;
  import javax.naming.directory.SearchResult;
  import javax.naming.ldap.Control;
  import javax.naming.ldap.InitialLdapContext;
  import javax.security.auth.Subject;
  import javax.security.auth.login.LoginContext;
  import javax.security.auth.login.LoginException;
  import javax.security.auth.spi.LoginModule;
  
  public class TestKerberizedLdap implements Runnable {
  
      public static void main(String[] args) {
          Runnable rn = null;
          if (args.length == 1) {
              rn = new TestKerberizedLdap(args[0]);
          }
          else if (args.length == 0) {
              rn = new TestKerberizedLdap("C:/dev/projects/TestKerberizedLdap/src/testkerberizedldap.properties");
          }
          else {
              printUsage();
              return;
          }
  
          rn.run();
      }
  
      private static final String loginModuleClassName = "com.sun.security.auth.module.Krb5LoginModule";
      private static final String koid = "1.2.840.113554.1.2.2";
      private static final String soid = "1.3.6.1.5.5.2";
  
      private Subject subject;
      private DirContext ldapContext;
      private String username;
      private String password;
      private String ldapHost;
  
      private TestKerberizedLdap(String propFileName) {
          Properties prop = new Properties();
          try {
              FileInputStream iStream = new FileInputStream(propFileName);
              prop.load(iStream);
              iStream.close();
          }
          catch (IOException e) {
              e.printStackTrace();
          }
  
          System.out.println("Properties found:");
          for (String propName : prop.stringPropertyNames()) {
              System.out.println(propName + " = " + prop.getProperty(propName));
          }
          System.out.println();
  
          System.setProperty("java.security.krb5.conf", prop.getProperty("krbConf"));
          System.setProperty("java.security.krb5.debug", "true");
          System.setProperty("sun.security.krb5.debug", "true");
  
          ldapHost = prop.getProperty("ldapHost");
          username = prop.getProperty("user");
          password = prop.getProperty("password");
          subject = null;
          ldapContext = null;
      }
  
      public void run() {
          try {
              initSubject();
              if (subject != null) {
                  initContextKerberized();
              }
          }
          catch (Exception ex) {
              ex.printStackTrace();
          }
      }
  
      private void initContextKerberized() throws Exception {
          Subject.doAs(subject, new PrivilegedExceptionAction&lt;Object&gt;() {
              public Object run() throws Exception {
                  Hashtable&lt;String, String&gt; env = new Hashtable&lt;String, String&gt;();
                  env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
                  env.put(Context.PROVIDER_URL, ldapHost);
                  env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
                  ldapContext = new InitialLdapContext(env, new Control[0]);
  
                  // Do stuff with ldapContext here...
                  searchLdapDirectory();
  
                  return null;
              }
          });
      }
  
      private void searchLdapDirectory() throws NamingException {
          String base = "CN=Users";
          String filter = "(objectclass=user)";
          SearchControls sc = new SearchControls();
          sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
          NamingEnumeration&lt;SearchResult&gt; ne = ldapContext.search(base, filter, sc);
          int numElements = 0;
          System.out.println();
          while (ne.hasMoreElements()) {
              SearchResult sr = ne.nextElement();
              Attributes attr = sr.getAttributes();
              System.out.println(attr.get("name").get());
              numElements++;
          }
          System.out.println("The number of elements returned was " + numElements);
      }
  
      private void initSubject() throws InstantiationException, ClassNotFoundException, IllegalAccessException {
          LoginModule module = null;
          try {
              module = (LoginModule) Class.forName(loginModuleClassName).newInstance();
              subject = new Subject();
              Map&lt;String, String&gt; options = new HashMap&lt;String, String&gt;();
              Map&lt;String, Object&gt; sharedState = new HashMap&lt;String, Object&gt;();
  
              if ((username != null) &amp;&amp; (password != null)) {
                  sharedState.put("javax.security.auth.login.password", password.toCharArray());
                  sharedState.put("javax.security.auth.login.name", username);
                  options.put("principal", username);
                  options.put("storeKey", "true");
                  options.put("useFirstPass", "true");
              }
              else {
                  options.put("principal", username);
                  options.put("useTicketCache", "true");
                  options.put("doNotPrompt", "true");
                  options.put("renewTGT", "true");
              }
              options.put("debug", "true");
              options.put("refreshKrb5Config", "true");
  
              module.initialize(subject, null, sharedState, options);
              module.login();
              module.commit();
          }
          catch (LoginException ex) {
              ex.printStackTrace();
              subject = null;
              if (module != null) {
                  try {
                      module.abort();
                  }
                  catch (LoginException ex2) {
                      ex2.printStackTrace();
                  }
              }
          }
      }
  
      private static void printUsage() {
          System.out.println();
          System.out.println("Usage: TestKerberizedLdap &lt;property file name&gt;");
          System.out.println();
      }
  }
  </code></pre>
  
  <p>Within the testkerberized.properties file, the "ldapHost" property is defined to be "ldap://dv2k8ad.2k8.hlp.net:389/dc=2k8,dc=hlp,dc=net".</p>
  
  <p>Now, when I run this app with Java 7, it works like a charm. The Kerberos authentication succeeds, and the data is successfully retrieved from the LDAP server. However, I cant get it to run with Java 6. It keeps giving me the following exception:</p>
  
  <pre><code>    Found ticket for user1@2K8.HLP.NET to go to krbtgt/2K8.HLP.NET@2K8.HLP.NET expiring on Thu Jun 04 00:47:07 PDT 2015
  Entered Krb5Context.initSecContext with state=STATE_NEW
  Found ticket for user1@2K8.HLP.NET to go to krbtgt/2K8.HLP.NET@2K8.HLP.NET expiring on Thu Jun 04 00:47:07 PDT 2015
  Service ticket not found in the subject
  &gt;&gt;&gt; Credentials acquireServiceCreds: same realm
  default etypes for default_tgs_enctypes: 23 16 3 1.
  &gt;&gt;&gt; CksumType: sun.security.krb5.internal.crypto.RsaMd5CksumType
  &gt;&gt;&gt; EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
  &gt;&gt;&gt; KrbKdcReq send: kdc=172.23.5.151 UDP:88, timeout=30000, number of retries =3, #bytes=1289
  &gt;&gt;&gt; KDCCommunication: kdc=172.23.5.151 UDP:88, timeout=30000,Attempt =1, #bytes=1289
  &gt;&gt;&gt; KrbKdcReq send: #bytes read=92
  &gt;&gt;&gt; KrbKdcReq send: #bytes read=92
  &gt;&gt;&gt; KdcAccessibility: remove 172.23.5.151:88
  &gt;&gt;&gt; KDCRep: init() encoding tag is 126 req type is 13
  &gt;&gt;&gt;KRBError:
       sTime is Wed Jun 03 14:47:07 PDT 2015 1433368027000
       suSec is 109093
       error code is 7
       error Message is Server not found in Kerberos database
       realm is 2K8.HLP.NET
       sname is ldap/2k8.hlp.net
       msgType is 30
  java.security.PrivilegedActionException: javax.naming.AuthenticationException: GSSAPI [Root exception is javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Server not found in Kerberos database (7))]]
      at java.security.AccessController.doPrivileged(Native Method)
      at javax.security.auth.Subject.doAs(Subject.java:396)
      at TestKerberizedLdap.initContextKerberized(TestKerberizedLdap.java:95)
      at TestKerberizedLdap.run(TestKerberizedLdap.java:85)
      at TestKerberizedLdap.main(TestKerberizedLdap.java:39)
  Caused by: javax.naming.AuthenticationException: GSSAPI [Root exception is javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Server not found in Kerberos database (7))]]
      at com.sun.jndi.ldap.sasl.LdapSasl.saslBind(LdapSasl.java:150)
      at com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:214)
      at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2694)
      at com.sun.jndi.ldap.LdapCtx.&lt;init&gt;(LdapCtx.java:293)
      at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:175)
      at com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(LdapCtxFactory.java:193)
      at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:136)
      at com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(LdapCtxFactory.java:66)
      at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:667)
      at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:288)
      at javax.naming.InitialContext.init(InitialContext.java:223)
      at javax.naming.ldap.InitialLdapContext.&lt;init&gt;(InitialLdapContext.java:134)
      at TestKerberizedLdap$1.run(TestKerberizedLdap.java:101)
      ... 5 more
  Caused by: javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Server not found in Kerberos database (7))]
      at com.sun.security.sasl.gsskerb.GssKrb5Client.evaluateChallenge(GssKrb5Client.java:194)
      at com.sun.jndi.ldap.sasl.LdapSasl.saslBind(LdapSasl.java:105)
      ... 17 more
  Caused by: GSSException: No valid credentials provided (Mechanism level: Server not found in Kerberos database (7))
      at sun.security.jgss.krb5.Krb5Context.initSecContext(Krb5Context.java:663)
      at sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:230)
      at sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:162)
      at com.sun.security.sasl.gsskerb.GssKrb5Client.evaluateChallenge(GssKrb5Client.java:175)
      ... 18 more
  Caused by: KrbException: Server not found in Kerberos database (7)
      at sun.security.krb5.KrbTgsRep.&lt;init&gt;(KrbTgsRep.java:61)
      at sun.security.krb5.KrbTgsReq.getReply(KrbTgsReq.java:185)
      at sun.security.krb5.internal.CredentialsUtil.serviceCreds(CredentialsUtil.java:294)
      at sun.security.krb5.internal.CredentialsUtil.acquireServiceCreds(CredentialsUtil.java:106)
      at sun.security.krb5.Credentials.acquireServiceCreds(Credentials.java:557)
      at sun.security.jgss.krb5.Krb5Context.initSecContext(Krb5Context.java:594)
      ... 21 more
  Caused by: KrbException: Identifier doesn't match expected value (906)
      at sun.security.krb5.internal.KDCRep.init(KDCRep.java:133)
      at sun.security.krb5.internal.TGSRep.init(TGSRep.java:58)
      at sun.security.krb5.internal.TGSRep.&lt;init&gt;(TGSRep.java:53)
      at sun.security.krb5.KrbTgsRep.&lt;init&gt;(KrbTgsRep.java:46)
      ... 26 more
  </code></pre>
  
  <p>It's worth noting that module.commit() succeeded, and the subject was successfully obtained, so no issues there. But I noticed that the sname field in the KrbError message was off. It says ldap/2k8.hlp.net whereas it should have been ldap/dv2k8ad.2k8.hlp.net.</p>
  
  <p>Following this, I ran a Wireshark session to capture all the Kerberos packets being sent, and I captured packets while running TestKerberizedLdap on both Java 6 and Java 7. The TGT was acquired successfully, because the program got a successful AS-REP in both cases. However, the TGS-REQ being sent to the KDC had different values. The server name sent by Java 6 was ldap/2k8.hlp.net and the one sent by Java 7 was ldap/dv2k8ad.2k8.hlp.net</p>
  
  <p>I dont know why Java 6 is choosing to trim the LDAP host name. If anyone knows a workaround, or a fix (short of updating Java), I'd be really grateful for any help.</p>