You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by pl...@apache.org on 2016/05/11 05:19:07 UTC

directory-kerby git commit: DIRKRB-567 Support multiple KDCs for a given realm in client.

Repository: directory-kerby
Updated Branches:
  refs/heads/trunk 8d1694b78 -> 4bd0fb91f


DIRKRB-567 Support multiple KDCs for a given realm in client.


Project: http://git-wip-us.apache.org/repos/asf/directory-kerby/repo
Commit: http://git-wip-us.apache.org/repos/asf/directory-kerby/commit/4bd0fb91
Tree: http://git-wip-us.apache.org/repos/asf/directory-kerby/tree/4bd0fb91
Diff: http://git-wip-us.apache.org/repos/asf/directory-kerby/diff/4bd0fb91

Branch: refs/heads/trunk
Commit: 4bd0fb91fbdba4f3230e63309227affd6e6a6498
Parents: 8d1694b
Author: plusplusjiajia <ji...@intel.com>
Authored: Wed May 11 13:24:23 2016 +0800
Committer: plusplusjiajia <ji...@intel.com>
Committed: Wed May 11 13:24:23 2016 +0800

----------------------------------------------------------------------
 kerby-dist/tool-dist/conf/krb5.conf             |   5 +
 .../kerby/kerberos/kerb/client/ClientUtil.java  | 108 +++++++++++++++++--
 .../kerby/kerberos/kerb/client/KrbConfig.java   |  32 ++++++
 .../kerby/kerberos/kerb/client/KrbHandler.java  |   9 +-
 .../client/impl/DefaultInternalKrbClient.java   |  51 +++++++--
 .../kerb/client/impl/DefaultKrbHandler.java     |   4 +-
 .../kerberos/kerb/client/KrbConfigLoadTest.java |   4 +-
 .../kerby/kerberos/kerb/common/Krb5Conf.java    |  14 ++-
 .../kerby/kerberos/kerb/common/Krb5Parser.java  |  35 ++++--
 .../kerby/kerberos/kerb/Krb5ParserTest.java     |  10 +-
 .../kerby/kerberos/kerb/KrbErrorCode.java       |   4 +-
 .../kerby/kerberos/kerb/server/KdcHandler.java  |   9 +-
 .../kerb/server/preauth/token/TokenPreauth.java |   4 +-
 .../kerberos/kerb/server/SimpleKdcServer.java   |   3 +-
 14 files changed, 252 insertions(+), 40 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/4bd0fb91/kerby-dist/tool-dist/conf/krb5.conf
----------------------------------------------------------------------
diff --git a/kerby-dist/tool-dist/conf/krb5.conf b/kerby-dist/tool-dist/conf/krb5.conf
index e857b84..8e024e3 100644
--- a/kerby-dist/tool-dist/conf/krb5.conf
+++ b/kerby-dist/tool-dist/conf/krb5.conf
@@ -22,3 +22,8 @@
     kdc_udp_port = 88
     kdc_tcp_port = 88
     pkinit_anchors = /etc/krb5/cacert.pem
+
+[realms]
+    EXAMPLE.COM = {
+        kdc = localhost:88
+    }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/4bd0fb91/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/ClientUtil.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/ClientUtil.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/ClientUtil.java
index 49b7666..f3dbc44 100644
--- a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/ClientUtil.java
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/ClientUtil.java
@@ -21,15 +21,20 @@ package org.apache.kerby.kerberos.kerb.client;
 
 import org.apache.kerby.kerberos.kerb.KrbException;
 import org.apache.kerby.kerberos.kerb.transport.TransportPair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.File;
 import java.io.IOException;
 import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 
 public final class ClientUtil {
     private ClientUtil() { }
 
+    private static final Logger LOG = LoggerFactory.getLogger(ClientUtil.class);
     private static final String KRB5_FILE_NAME = "krb5.conf";
     private static final String KRB5_ENV_NAME = "KRB5_CONFIG";
 
@@ -104,24 +109,115 @@ public final class ClientUtil {
     /**
      * Get KDC network transport addresses according to krb client setting.
      * @param setting The krb setting
+     * @param kdcString The kdc string, may include the port number
      * @return UDP and TCP addresses pair
      * @throws KrbException e
      */
     public static TransportPair getTransportPair(
-            KrbSetting setting) throws KrbException {
+            KrbSetting setting, String kdcString) throws KrbException, IOException {
         TransportPair result = new TransportPair();
-
         int tcpPort = setting.checkGetKdcTcpPort();
+        int udpPort = setting.checkGetKdcUdpPort();
+
+        int port = 0;
+        String kdc;
+        String portStr = null;
+
+        // Explicit IPv6 in []
+        if (kdcString.charAt(0) == '[') {
+            int pos = kdcString.indexOf(']', 1);
+            if (pos == -1) {
+                throw new IOException("Illegal KDC: " + kdcString);
+            }
+            kdc = kdcString.substring(1, pos);
+            // with port number
+            if (pos != kdcString.length() - 1) {
+                if (kdcString.charAt(pos + 1) != ':') {
+                    throw new IOException("Illegal KDC: " + kdcString);
+                }
+                portStr = kdcString.substring(pos + 2);
+            }
+        } else {
+            int colon = kdcString.indexOf(':');
+            // Hostname or IPv4 host only
+            if (colon == -1) {
+                kdc = kdcString;
+            } else {
+                int nextColon = kdcString.indexOf(':', colon + 1);
+                // >=2 ":", IPv6 with no port
+                if (nextColon > 0) {
+                    kdc = kdcString;
+                } else {
+                    // 1 ":", hostname or IPv4 with port
+                    kdc = kdcString.substring(0, colon);
+                    portStr = kdcString.substring(colon + 1);
+                }
+            }
+        }
+        if (portStr != null) {
+            int tempPort = parsePositiveIntString(portStr);
+            if (tempPort > 0) {
+                port = tempPort;
+            }
+        }
+        if (port != 0) {
+            tcpPort = port;
+            udpPort = port;
+        }
         if (tcpPort > 0) {
             result.tcpAddress = new InetSocketAddress(
-                    setting.getKdcHost(), tcpPort);
+                    kdc, tcpPort);
         }
-        int udpPort = setting.checkGetKdcUdpPort();
         if (udpPort > 0) {
             result.udpAddress = new InetSocketAddress(
-                    setting.getKdcHost(), udpPort);
+                    kdc, udpPort);
         }
-
         return result;
     }
+
+    private static int parsePositiveIntString(String intString) {
+        if (intString == null) {
+            return -1;
+        }
+        int ret = -1;
+        try {
+            ret = Integer.parseInt(intString);
+        } catch (Exception exc) {
+            return -1;
+        }
+        if (ret >= 0) {
+            return ret;
+        }
+        return -1;
+    }
+
+    /**
+     * Returns a list of KDC
+     *
+     * @throws KrbException if there's no way to find KDC for the realm
+     * @return the list of KDC, always non null
+     */
+    public static List<String> getKDCList(KrbSetting krbSetting) throws KrbException {
+
+        List<String> kdcList = new ArrayList<>();
+        kdcList.add(krbSetting.getKdcHost());
+        /*get the kdc realm */
+        String realm = krbSetting.getKdcRealm();
+        if (realm != null) {
+            KrbConfig krbConfig = krbSetting.getKrbConfig();
+            List<Object> kdcs = krbConfig.getRealmSectionItems(realm, "kdc");
+            if (kdcs != null) {
+                for (Object object : kdcs) {
+                    kdcList.add(object != null ? object.toString() : null);
+                }
+            }
+
+            if (kdcList == null) {
+                LOG.info("Cannot get kdc for realm " + realm);
+            }
+        } else {
+            throw new KrbException("Can't get the realm");
+        }
+        return kdcList;
+    }
 }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/4bd0fb91/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbConfig.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbConfig.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbConfig.java
index 37161bf..dbbc64c 100644
--- a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbConfig.java
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbConfig.java
@@ -22,14 +22,17 @@ package org.apache.kerby.kerberos.kerb.client;
 import org.apache.kerby.kerberos.kerb.common.Krb5Conf;
 import org.apache.kerby.kerberos.kerb.type.base.EncryptionType;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Kerb client side configuration API.
  */
 public class KrbConfig extends Krb5Conf {
     private static final String LIBDEFAULT = "libdefaults";
+    private static final String REALMS = "realms";
 
     public boolean enableDebug() {
         return getBoolean(KrbConfigKey.KRB_DEBUG, true, LIBDEFAULT);
@@ -312,4 +315,33 @@ public class KrbConfig extends Krb5Conf {
         return getString(
                 KrbConfigKey.PKINIT_KDC_HOSTNAME, true, LIBDEFAULT);
     }
+
+    public List<Object> getRealmSectionItems(String realm, String key) {
+        Map<String, Object> map = getRealmSection(realm);
+        List<Object> items = null;
+        if (map != null) {
+            items = new ArrayList<>();
+            for (Map.Entry<String, Object> entry : map.entrySet()) {
+                if (entry.getKey().equals(key)) {
+                    items.add(entry.getValue());
+                }
+            }
+        }
+        return items;
+    }
+
+    public Map<String, Object> getRealmSection(String realm) {
+        Object realms = getSection(REALMS);
+        if (realms != null) {
+            Map<String, Object> map = (Map) realms;
+            for (Map.Entry<String, Object> entry : map.entrySet()) {
+                if (entry.getKey().equals(realm)) {
+                    return (Map) entry.getValue();
+                }
+            }
+            return null;
+        } else {
+            return null;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/4bd0fb91/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbHandler.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbHandler.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbHandler.java
index 1c6743f..1ec4e4d 100644
--- a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbHandler.java
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/KrbHandler.java
@@ -63,10 +63,13 @@ public abstract class KrbHandler {
      * Handle the kdc request.
      *
      * @param kdcRequest The kdc request
+     * @param tryNextKdc try next kdc or not
      * @throws KrbException e
      */
-    public void handleRequest(KdcRequest kdcRequest) throws KrbException {
-        kdcRequest.process();
+    public void handleRequest(KdcRequest kdcRequest, boolean tryNextKdc) throws KrbException {
+        if (!tryNextKdc) {
+            kdcRequest.process();
+        }
         KdcReq kdcReq = kdcRequest.getKdcReq();
         int bodyLen = kdcReq.encodingLength();
         KrbTransport transport = (KrbTransport) kdcRequest.getSessionData();
@@ -133,7 +136,7 @@ public abstract class KrbHandler {
                 kdcRequest.setEncryptionTypes(encryptionTypes);
                 kdcRequest.setPreauthRequired(true);
                 kdcRequest.resetPrequthContxt();
-                handleRequest(kdcRequest);
+                handleRequest(kdcRequest, false);
                 LOG.info("Retry with the new kdc request including pre-authentication.");
             } else {
                 LOG.info(error.getErrorCode().getMessage());

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/4bd0fb91/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/impl/DefaultInternalKrbClient.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/impl/DefaultInternalKrbClient.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/impl/DefaultInternalKrbClient.java
index df4ed10..06c6a7f 100644
--- a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/impl/DefaultInternalKrbClient.java
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/impl/DefaultInternalKrbClient.java
@@ -30,13 +30,18 @@ import org.apache.kerby.kerberos.kerb.transport.KrbTransport;
 import org.apache.kerby.kerberos.kerb.transport.TransportPair;
 import org.apache.kerby.kerberos.kerb.type.ticket.SgtTicket;
 import org.apache.kerby.kerberos.kerb.type.ticket.TgtTicket;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
 
 /**
  * A default krb client implementation.
  */
 public class DefaultInternalKrbClient extends AbstractInternalKrbClient {
+    private static final Logger LOG = LoggerFactory.getLogger(DefaultInternalKrbClient.class);
 
     private DefaultKrbHandler krbHandler;
     private KrbTransport transport;
@@ -57,21 +62,49 @@ public class DefaultInternalKrbClient extends AbstractInternalKrbClient {
     }
 
     private void doRequest(KdcRequest request) throws KrbException {
-        try {
-            TransportPair tpair = ClientUtil.getTransportPair(getSetting());
-            KrbNetwork network = new KrbNetwork();
 
-            network.setSocketTimeout(getSetting().getTimeout());
+        List<String> kdcList = ClientUtil.getKDCList(getSetting());
 
-            transport = network.connect(tpair);
+        // tempKdc may include the port number
+        Iterator<String> tempKdc = kdcList.iterator();
+        if (!tempKdc.hasNext()) {
+            throw new KrbException("Cannot get kdc for realm " + getSetting().getKdcRealm());
+        }
 
-            request.setSessionData(transport);
-            krbHandler.handleRequest(request);
-        } catch (IOException e) {
-            throw new KrbException("Failed to create transport", e);
+        try {
+            sendIfPossible(request, tempKdc.next(), getSetting(), false);
+            LOG.info("Send to kdc success.");
+        } catch (Exception first) {
+            boolean ok = false;
+            while (tempKdc.hasNext()) {
+                try {
+                    sendIfPossible(request, tempKdc.next(), getSetting(), true);
+                    ok = true;
+                    LOG.info("Send to kdc success.");
+                    break;
+                } catch (Exception ignore) {
+                    LOG.info("ignore this kdc");
+                }
+            }
+            if (!ok) {
+                throw new KrbException("Failed to create transport", first);
+            }
         } finally {
             transport.release();
         }
+
+    }
+
+    private void sendIfPossible(KdcRequest request, String kdcString, KrbSetting setting,
+                                boolean tryNextKdc)
+        throws KrbException, IOException {
+
+        TransportPair tpair = ClientUtil.getTransportPair(setting, kdcString);
+        KrbNetwork network = new KrbNetwork();
+        network.setSocketTimeout(setting.getTimeout());
+        transport = network.connect(tpair);
+        request.setSessionData(transport);
+        krbHandler.handleRequest(request, tryNextKdc);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/4bd0fb91/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/impl/DefaultKrbHandler.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/impl/DefaultKrbHandler.java b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/impl/DefaultKrbHandler.java
index 246f399..8da5970 100644
--- a/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/impl/DefaultKrbHandler.java
+++ b/kerby-kerb/kerb-client/src/main/java/org/apache/kerby/kerberos/kerb/client/impl/DefaultKrbHandler.java
@@ -33,11 +33,11 @@ public class DefaultKrbHandler extends KrbHandler {
      * {@inheritDoc}
      */
     @Override
-    public void handleRequest(KdcRequest kdcRequest) throws KrbException {
+    public void handleRequest(KdcRequest kdcRequest, boolean tryNextKdc) throws KrbException {
         KrbTransport transport = (KrbTransport) kdcRequest.getSessionData();
         transport.setAttachment(kdcRequest);
 
-        super.handleRequest(kdcRequest);
+        super.handleRequest(kdcRequest, tryNextKdc);
         ByteBuffer receivedMessage = null;
         try {
             receivedMessage = transport.receiveMessage();

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/4bd0fb91/kerby-kerb/kerb-client/src/test/java/org/apache/kerby/kerberos/kerb/client/KrbConfigLoadTest.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-client/src/test/java/org/apache/kerby/kerberos/kerb/client/KrbConfigLoadTest.java b/kerby-kerb/kerb-client/src/test/java/org/apache/kerby/kerberos/kerb/client/KrbConfigLoadTest.java
index cfd3929..50ee72b 100644
--- a/kerby-kerb/kerb-client/src/test/java/org/apache/kerby/kerberos/kerb/client/KrbConfigLoadTest.java
+++ b/kerby-kerb/kerb-client/src/test/java/org/apache/kerby/kerberos/kerb/client/KrbConfigLoadTest.java
@@ -25,7 +25,7 @@ import org.junit.Test;
 import java.io.File;
 import java.net.URL;
 
-import static org.assertj.core.api.Assertions.*;
+import static org.assertj.core.api.Assertions.assertThat;
 
 /**
  * Test for loading configurations form krb5.conf.
@@ -61,5 +61,7 @@ public class KrbConfigLoadTest {
         assertThat(krbConfig.getPkinitAnchors()).hasSize(1);
         assertThat(krbConfig.getPkinitIdentities()).hasSize(2);
         assertThat(krbConfig.getPkinitKdcHostName()).isEqualTo("kdc-server.example.com");
+        assertThat(krbConfig.getRealmSection("ATHENA.MIT.EDU")).hasSize(3);
+        assertThat(krbConfig.getRealmSectionItems("ATHENA.MIT.EDU", "admin_server")).hasSize(1);
     }
 }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/4bd0fb91/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/common/Krb5Conf.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/common/Krb5Conf.java b/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/common/Krb5Conf.java
index 1834ae5..1dba876 100644
--- a/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/common/Krb5Conf.java
+++ b/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/common/Krb5Conf.java
@@ -41,11 +41,12 @@ public class Krb5Conf extends Conf {
      * of config value(string list).
      */
     private static final String LIST_SPLITTER = " |,";
+    private Map<String, Object> krb5Map;
 
     public void addKrb5Config(File krb5File) throws IOException {
         Krb5Parser krb5Parser = new Krb5Parser(krb5File);
         krb5Parser.load();
-        Map<String, Object> krb5Map = krb5Parser.getItems();
+        krb5Map = krb5Parser.getItems();
         addResource(Resource.createMapResource(krb5Map));
     }
 
@@ -162,4 +163,15 @@ public class Krb5Conf extends Conf {
         String[] values = value.split(LIST_SPLITTER);
         return values;
     }
+
+    protected Object getSection(String sectionName) {
+        if (krb5Map != null) {
+            for (Map.Entry<String, Object> entry : krb5Map.entrySet()) {
+                if (entry.getKey().equals(sectionName)) {
+                    return entry.getValue();
+                }
+            }
+        }
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/4bd0fb91/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/common/Krb5Parser.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/common/Krb5Parser.java b/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/common/Krb5Parser.java
index 1494377..9f4196c 100644
--- a/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/common/Krb5Parser.java
+++ b/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/common/Krb5Parser.java
@@ -26,7 +26,7 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -57,7 +57,7 @@ public class Krb5Parser {
     public void load() throws IOException {
         BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(krb5conf),
                 StandardCharsets.UTF_8));
-        items = new HashMap<String, Object>();
+        items = new IdentityHashMap<>();
 
         String originLine = br.readLine();
         while (originLine != null) {
@@ -97,11 +97,32 @@ public class Krb5Parser {
     /**
      * Get the contents of a section given the section name.
      * @param sectionName the name of a section
+     * @param keys the keys list
      * @return a Map of section contents
      */
-    public Map<String, Object> getSection(String sectionName) {
-        Map<String, Object> sections = (HashMap) items.get(sectionName);
-        return sections;
+    public Object getSection(String sectionName, String ... keys) {
+        Object value = null;
+        for (Map.Entry<String, Object> item : items.entrySet()) {
+            if (item.getKey().equals(sectionName)) {
+                value = item.getValue();
+                Map<String, Object> map = (Map) item.getValue();
+                for (Map.Entry<String, Object> entry : map.entrySet()) {
+                    if (entry.getKey().equals(keys[0])) {
+                        value = entry.getValue();
+                    }
+                }
+            }
+        }
+
+        for (int i = 1; i < keys.length; i++) {
+            Map<String, Object> map = (Map) value;
+            for (Map.Entry<String, Object> entry : map.entrySet()) {
+                if (entry.getKey().equals(keys[i])) {
+                    value = entry.getValue();
+                }
+            }
+        }
+        return value;
     }
 
     /**
@@ -118,7 +139,7 @@ public class Krb5Parser {
     private void insertSections(String line, BufferedReader br, Map<String, Object> items) throws IOException {
         while (line.startsWith("[")) {
             String sectionName = line.substring(1, line.length() - 1);
-            Map<String, Object> entries = new HashMap<String, Object>();
+            Map<String, Object> entries = new IdentityHashMap<>();
             line = br.readLine();
             if (line == null) {
                 break;
@@ -174,7 +195,7 @@ public class Krb5Parser {
         kv[1] = kv[1].trim();
 
         if (kv[1].startsWith("{")) {
-            Map<String, Object> meValue = new HashMap<String, Object>();
+            Map<String, Object> meValue = new IdentityHashMap<>();
             line = br.readLine();
             if (line != null) {
                 line = line.trim();

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/4bd0fb91/kerby-kerb/kerb-common/src/test/java/org/apache/kerby/kerberos/kerb/Krb5ParserTest.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-common/src/test/java/org/apache/kerby/kerberos/kerb/Krb5ParserTest.java b/kerby-kerb/kerb-common/src/test/java/org/apache/kerby/kerberos/kerb/Krb5ParserTest.java
index b11ad16..fb09722 100644
--- a/kerby-kerb/kerb-common/src/test/java/org/apache/kerby/kerberos/kerb/Krb5ParserTest.java
+++ b/kerby-kerb/kerb-common/src/test/java/org/apache/kerby/kerberos/kerb/Krb5ParserTest.java
@@ -45,11 +45,9 @@ public class Krb5ParserTest {
         assertThat(k.getSections().size()).isEqualTo(4);
         assertThat(k.getSections().contains("libdefaults")).isTrue();
 
-        assertThat(k.getSection("libdefaults").get("dns_lookup_kdc")).isEqualTo("false");
-        assertThat(k.getSection("realms").get("ATHENA.MIT.EDU") instanceof Map).isTrue();
-        Map<String, Object> m1 = (Map) k.getSection("realms").get("ATHENA.MIT.EDU");
-        assertThat(m1.get("v4_instance_convert") instanceof  Map).isTrue();
-        Map<String, Object> m2 = (Map) m1.get("v4_instance_convert");
-        assertThat(m2.get("mit")).isEqualTo("mit.edu");
+        assertThat(k.getSection("libdefaults", "dns_lookup_kdc")).isEqualTo("false");
+        assertThat(k.getSection("realms", "ATHENA.MIT.EDU") instanceof Map).isTrue();
+        assertThat(k.getSection("realms", "ATHENA.MIT.EDU", "v4_instance_convert") instanceof  Map).isTrue();
+        assertThat(k.getSection("realms", "ATHENA.MIT.EDU", "v4_instance_convert", "mit").equals("mit.edu"));
     }
 }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/4bd0fb91/kerby-kerb/kerb-core/src/main/java/org/apache/kerby/kerberos/kerb/KrbErrorCode.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-core/src/main/java/org/apache/kerby/kerberos/kerb/KrbErrorCode.java b/kerby-kerb/kerb-core/src/main/java/org/apache/kerby/kerberos/kerb/KrbErrorCode.java
index cd4ad1e..b7f3df3 100644
--- a/kerby-kerb/kerb-core/src/main/java/org/apache/kerby/kerberos/kerb/KrbErrorCode.java
+++ b/kerby-kerb/kerb-core/src/main/java/org/apache/kerby/kerberos/kerb/KrbErrorCode.java
@@ -95,8 +95,10 @@ public enum KrbErrorCode implements EnumType {
     KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED(79, "PA checksum must be included"),
     KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED(80, "Digest in signed data not accepted"),
     KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED(81, "Public key encryption not supported"),
+    TOKEN_PREAUTH_NOT_ALLOWED(82, "Token preauth is not allowed"),
 
-    KRB_TIMEOUT(5000, "Network timeout");
+    KRB_TIMEOUT(5000, "Network timeout"),
+    UNKNOWN_ERR(5001, "Unknow error");
 
     private final int value;
     private final String message;

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/4bd0fb91/kerby-kerb/kerb-server/src/main/java/org/apache/kerby/kerberos/kerb/server/KdcHandler.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-server/src/main/java/org/apache/kerby/kerberos/kerb/server/KdcHandler.java b/kerby-kerb/kerb-server/src/main/java/org/apache/kerby/kerberos/kerb/server/KdcHandler.java
index aa896c2..8a1a21a 100644
--- a/kerby-kerb/kerb-server/src/main/java/org/apache/kerby/kerberos/kerb/server/KdcHandler.java
+++ b/kerby-kerb/kerb-server/src/main/java/org/apache/kerby/kerberos/kerb/server/KdcHandler.java
@@ -87,7 +87,8 @@ public class KdcHandler {
             String realm = getRequestRealm(kdcReq);
             if (realm == null || !kdcContext.getKdcRealm().equals(realm)) {
                 LOG.error("Invalid realm from kdc request: " + realm);
-                throw new KrbException("Invalid realm from kdc request: " + realm);
+                throw new KrbException(KrbErrorCode.WRONG_REALM,
+                    "Invalid realm from kdc request: " + realm);
             }
 
             if (messageType == KrbMessageType.TGS_REQ) {
@@ -122,7 +123,11 @@ public class KdcHandler {
                 KrbError krbError = new KrbError();
                 krbError.setStime(KerberosTime.now());
                 krbError.setSusec(100);
-                krbError.setErrorCode(e.getKrbErrorCode());
+                if (e.getKrbErrorCode() != null) {
+                    krbError.setErrorCode(e.getKrbErrorCode());
+                } else {
+                    krbError.setErrorCode(KrbErrorCode.UNKNOWN_ERR);
+                }
                 krbError.setCrealm(kdcContext.getKdcRealm());
                 if (kdcRequest.getClientPrincipal() != null) {
                     krbError.setCname(kdcRequest.getClientPrincipal());

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/4bd0fb91/kerby-kerb/kerb-server/src/main/java/org/apache/kerby/kerberos/kerb/server/preauth/token/TokenPreauth.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-server/src/main/java/org/apache/kerby/kerberos/kerb/server/preauth/token/TokenPreauth.java b/kerby-kerb/kerb-server/src/main/java/org/apache/kerby/kerberos/kerb/server/preauth/token/TokenPreauth.java
index 11e9b6f..f4580fc 100644
--- a/kerby-kerb/kerb-server/src/main/java/org/apache/kerby/kerberos/kerb/server/preauth/token/TokenPreauth.java
+++ b/kerby-kerb/kerb-server/src/main/java/org/apache/kerby/kerberos/kerb/server/preauth/token/TokenPreauth.java
@@ -20,6 +20,7 @@
 package org.apache.kerby.kerberos.kerb.server.preauth.token;
 
 import org.apache.kerby.kerberos.kerb.KrbCodec;
+import org.apache.kerby.kerberos.kerb.KrbErrorCode;
 import org.apache.kerby.kerberos.kerb.KrbException;
 import org.apache.kerby.kerberos.kerb.KrbRuntime;
 import org.apache.kerby.kerberos.kerb.common.EncryptionUtil;
@@ -62,7 +63,8 @@ public class TokenPreauth extends AbstractPreauthPlugin {
                           PaDataEntry paData) throws KrbException {
 
         if (!kdcRequest.getKdcContext().getConfig().isAllowTokenPreauth()) {
-            throw new KrbException("Token preauth is not allowed.");
+            throw new KrbException(KrbErrorCode.TOKEN_PREAUTH_NOT_ALLOWED,
+                "Token preauth is not allowed.");
         }
         if (paData.getPaDataType() == PaDataType.TOKEN_REQUEST) {
             EncryptedData encData = KrbCodec.decode(paData.getPaDataValue(), EncryptedData.class);

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/4bd0fb91/kerby-kerb/kerb-simplekdc/src/main/java/org/apache/kerby/kerberos/kerb/server/SimpleKdcServer.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-simplekdc/src/main/java/org/apache/kerby/kerberos/kerb/server/SimpleKdcServer.java b/kerby-kerb/kerb-simplekdc/src/main/java/org/apache/kerby/kerberos/kerb/server/SimpleKdcServer.java
index 6f4fd63..74e4ec9 100644
--- a/kerby-kerb/kerb-simplekdc/src/main/java/org/apache/kerby/kerberos/kerb/server/SimpleKdcServer.java
+++ b/kerby-kerb/kerb-simplekdc/src/main/java/org/apache/kerby/kerberos/kerb/server/SimpleKdcServer.java
@@ -24,6 +24,7 @@ import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
 import org.apache.kerby.kerberos.kerb.admin.LocalKadminImpl;
 import org.apache.kerby.kerberos.kerb.client.Krb5Conf;
 import org.apache.kerby.kerberos.kerb.client.KrbClient;
+import org.apache.kerby.kerberos.kerb.client.KrbConfig;
 import org.apache.kerby.kerberos.kerb.client.KrbPkinitClient;
 import org.apache.kerby.kerberos.kerb.client.KrbTokenClient;
 import org.apache.kerby.util.NetworkUtil;
@@ -51,7 +52,7 @@ public class SimpleKdcServer extends KdcServer {
      */
     public SimpleKdcServer() throws KrbException {
         super();
-        this.krbClnt = new KrbClient();
+        this.krbClnt = new KrbClient(new KrbConfig());
 
         setKdcRealm("EXAMPLE.COM");
         setKdcHost("localhost");