8036779: sun.security.krb5.KdcComm interprets kdc_timeout as msec instead of sec
Reviewed-by: xuelei
diff --git a/src/share/classes/sun/security/krb5/KdcComm.java b/src/share/classes/sun/security/krb5/KdcComm.java
index caaf20a..721c105 100644
--- a/src/share/classes/sun/security/krb5/KdcComm.java
+++ b/src/share/classes/sun/security/krb5/KdcComm.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -144,7 +144,8 @@
         try {
             Config cfg = Config.getInstance();
             String temp = cfg.get("libdefaults", "kdc_timeout");
-            timeout = parsePositiveIntString(temp);
+            timeout = parseTimeString(temp);
+
             temp = cfg.get("libdefaults", "max_retries");
             max_retries = parsePositiveIntString(temp);
             temp = cfg.get("libdefaults", "udp_preference_limit");
@@ -426,6 +427,25 @@
     }
 
     /**
+     * Parses a time value string. If it ends with "s", parses as seconds.
+     * Otherwise, parses as milliseconds.
+     * @param s the time string
+     * @return the integer value in milliseconds, or -1 if input is null or
+     * has an invalid format
+     */
+    private static int parseTimeString(String s) {
+        if (s == null) {
+            return -1;
+        }
+        if (s.endsWith("s")) {
+            int seconds = parsePositiveIntString(s.substring(0, s.length()-1));
+            return (seconds < 0) ? -1 : (seconds*1000);
+        } else {
+            return parsePositiveIntString(s);
+        }
+    }
+
+    /**
      * Returns krb5.conf setting of {@code key} for a specific realm,
      * which can be:
      * 1. defined in the sub-stanza for the given realm inside [realms], or
@@ -446,7 +466,11 @@
         try {
             String value =
                Config.getInstance().get("realms", realm, key);
-            temp = parsePositiveIntString(value);
+            if (key.equals("kdc_timeout")) {
+                temp = parseTimeString(value);
+            } else {
+                temp = parsePositiveIntString(value);
+            }
         } catch (Exception exc) {
             // Ignored, defValue will be picked up
         }
diff --git a/test/sun/security/krb5/auto/KDC.java b/test/sun/security/krb5/auto/KDC.java
index 356f84e..686e238 100644
--- a/test/sun/security/krb5/auto/KDC.java
+++ b/test/sun/security/krb5/auto/KDC.java
@@ -141,6 +141,8 @@
     private BlockingQueue<Job> q = new ArrayBlockingQueue<>(100);
     // Options
     private Map<Option,Object> options = new HashMap<>();
+    // Realm-specific krb5.conf settings
+    private List<String> conf = new ArrayList<>();
 
     private Thread thread1, thread2, thread3;
     DatagramSocket u1 = null;
@@ -243,7 +245,7 @@
     /**
      * Sets an option
      * @param key the option name
-     * @param obj the value
+     * @param value the value
      */
     public void setOption(Option key, Object value) {
         if (value == null) {
@@ -373,6 +375,13 @@
     }
 
     /**
+     * Add realm-specific krb5.conf setting
+     */
+    public void addConf(String s) {
+        conf.add(s);
+    }
+
+    /**
      * Writes a krb5.conf for one or more KDC that includes KDC locations for
      * each realm and the default realm name. You can also add extra strings
      * into the file. The method should be called like:
@@ -397,6 +406,7 @@
      * [realms]
      *   REALM.NAME = {
      *     kdc = host:port_number
+     *     # realm-specific settings
      *   }
      * </pre>
      *
@@ -444,10 +454,10 @@
             }
         }
         sb.append("\n[realms]\n");
-        sb.append(realmLineForKDC(kdc));
+        sb.append(kdc.realmLine());
         for (Object o: more) {
             if (o instanceof KDC) {
-                sb.append(realmLineForKDC((KDC)o));
+                sb.append(((KDC)o).realmLine());
             }
         }
         FileOutputStream fos = new FileOutputStream(f);
@@ -1133,14 +1143,16 @@
 
     /**
      * Generates a line for a KDC to put inside [realms] of krb5.conf
-     * @param kdc the KDC
-     * @return REALM.NAME = { kdc = host:port }
+     * @return REALM.NAME = { kdc = host:port etc }
      */
-    private static String realmLineForKDC(KDC kdc) {
-        return String.format("%s = {\n    kdc = %s:%d\n}\n",
-                kdc.realm,
-                kdc.kdc,
-                kdc.port);
+    private String realmLine() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(realm).append(" = {\n    kdc = ")
+                .append(kdc).append(':').append(port).append('\n');
+        for (String s: conf) {
+            sb.append("    ").append(s).append('\n');
+        }
+        return sb.append("}\n").toString();
     }
 
     /**
diff --git a/test/sun/security/krb5/auto/UdpTcp.java b/test/sun/security/krb5/auto/UdpTcp.java
index e66f5d4..ae54b79 100644
--- a/test/sun/security/krb5/auto/UdpTcp.java
+++ b/test/sun/security/krb5/auto/UdpTcp.java
@@ -43,9 +43,15 @@
         OneKDC kdc = new OneKDC(null);
         kdc.writeJAASConf();
 
-        KDC.saveConfig(OneKDC.KRB5_CONF, kdc,
-                "udp_preference_limit = "
-                        + (args[0].equals("UDP") ? "1000" : "100"));
+        // Two styles of kdc_timeout setting. One global, one realm-specific.
+        if (args[0].equals("UDP")) {
+            KDC.saveConfig(OneKDC.KRB5_CONF, kdc,
+                    "kdc_timeout = 10s");
+        } else {
+            kdc.addConf("kdc_timeout = 10s");
+            KDC.saveConfig(OneKDC.KRB5_CONF, kdc,
+                    "udp_preference_limit = 1");
+        }
         Config.refresh();
 
         ByteArrayOutputStream bo = new ByteArrayOutputStream();
@@ -56,7 +62,7 @@
 
         for (String line: new String(bo.toByteArray()).split("\n")) {
             if (line.contains(">>> KDCCommunication")) {
-                if (!line.contains(args[0])) {
+                if (!line.contains(args[0]) || !line.contains("timeout=10000")) {
                     throw new Exception("No " + args[0] + " in: " + line);
                 }
             }