changeset 6058:33e29fbc3e5b

7184246: Simplify Config.get() of krb5 Reviewed-by: xuelei
author weijun
date Mon, 29 Oct 2012 14:14:06 +0800
parents 615af31cfccc
children cb70077c6370
files src/share/classes/sun/security/krb5/Checksum.java src/share/classes/sun/security/krb5/Config.java src/share/classes/sun/security/krb5/KdcComm.java src/share/classes/sun/security/krb5/PrincipalName.java src/share/classes/sun/security/krb5/Realm.java src/share/classes/sun/security/krb5/SCDynamicStoreConfig.java src/share/classes/sun/security/krb5/internal/KDCOptions.java src/share/classes/sun/security/krb5/internal/KerberosTime.java src/share/classes/sun/security/krb5/internal/crypto/CksumType.java src/share/classes/sun/security/krb5/internal/crypto/EType.java src/share/classes/sun/security/krb5/internal/ktab/KeyTab.java test/sun/security/krb5/ConfPlusProp.java test/sun/security/krb5/DnsFallback.java test/sun/security/krb5/ParseConfig.java test/sun/security/krb5/auto/BasicKrb5Test.java test/sun/security/krb5/auto/MaxRetries.java test/sun/security/krb5/config/Duplicates.java test/sun/security/krb5/config/SCDynamicConfigTest.java test/sun/security/krb5/config/k1.conf
diffstat 19 files changed, 594 insertions(+), 529 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/sun/security/krb5/Checksum.java	Sat Oct 27 09:18:29 2012 +0100
+++ b/src/share/classes/sun/security/krb5/Checksum.java	Mon Oct 29 14:14:06 2012 +0800
@@ -74,14 +74,18 @@
 
     private static boolean DEBUG = Krb5.DEBUG;
     static {
+        initStatic();
+    }
+
+    public static void initStatic() {
         String temp = null;
         Config cfg = null;
         try {
             cfg = Config.getInstance();
-            temp = cfg.getDefault("default_checksum", "libdefaults");
+            temp = cfg.get("libdefaults", "default_checksum");
             if (temp != null)
                 {
-                    CKSUMTYPE_DEFAULT = cfg.getType(temp);
+                    CKSUMTYPE_DEFAULT = Config.getType(temp);
                 } else {
                     /*
                      * If the default checksum is not
@@ -103,10 +107,10 @@
 
 
         try {
-            temp = cfg.getDefault("safe_checksum_type", "libdefaults");
+            temp = cfg.get("libdefaults", "safe_checksum_type");
             if (temp != null)
                 {
-                    SAFECKSUMTYPE_DEFAULT = cfg.getType(temp);
+                    SAFECKSUMTYPE_DEFAULT = Config.getType(temp);
                 } else {
                     SAFECKSUMTYPE_DEFAULT = CKSUMTYPE_RSA_MD5_DES;
                 }
--- a/src/share/classes/sun/security/krb5/Config.java	Sat Oct 27 09:18:29 2012 +0100
+++ b/src/share/classes/sun/security/krb5/Config.java	Mon Oct 29 14:14:06 2012 +0800
@@ -38,11 +38,14 @@
 import java.io.BufferedReader;
 import java.io.InputStreamReader;
 import java.io.IOException;
-import java.util.Enumeration;
 import java.util.StringTokenizer;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Locale;
 import sun.net.dns.ResolverConfiguration;
 import sun.security.krb5.internal.crypto.EType;
 import sun.security.krb5.internal.Krb5;
@@ -62,7 +65,7 @@
     /*
      * Hashtable used to store configuration infomation.
      */
-    private Hashtable<String,Object> stanzaTable;
+    private Hashtable<String,Object> stanzaTable = new Hashtable<>();
 
     private static boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG;
 
@@ -100,7 +103,9 @@
     /**
      * Refresh and reload the Configuration. This could involve,
      * for example reading the Configuration file again or getting
-     * the java.security.krb5.* system properties again.
+     * the java.security.krb5.* system properties again. This method
+     * also tries its best to update static fields in other classes
+     * that depend on the configuration.
      *
      * @exception KrbException if error occurs when constructing a Config
      * instance. Possible causes would be either of java.security.krb5.realm or
@@ -110,6 +115,8 @@
     public static synchronized void refresh() throws KrbException {
         singleton = new Config();
         KdcComm.initStatic();
+        EType.initStatic();
+        Checksum.initStatic();
     }
 
 
@@ -163,7 +170,7 @@
 
         // Always read the Kerberos configuration file
         try {
-            Vector<String> configFile;
+            List<String> configFile;
             String fileName = getJavaFileName();
             if (fileName != null) {
                 configFile = loadConfigFile(fileName);
@@ -194,61 +201,93 @@
                 }
             }
         } catch (IOException ioe) {
-            // No krb5.conf, no problem. We'll use DNS or system property etc.
+            // I/O error, mostly like krb5.conf missing.
+            // No problem. We'll use DNS or system property etc.
         }
     }
 
     /**
-     * Gets the default int value for the specified name.
-     * @param name the name.
-     * @return the default Integer, null is returned if no such name and
-     * value are found in configuration file, or error occurs when parsing
-     * string to integer.
+     * Gets the last-defined string value for the specified keys.
+     * @param keys the keys, as an array from section name, sub-section names
+     * (if any), to value name.
+     * @return the value. When there are multiple values for the same key,
+     * returns the last one. {@code null} is returned if not all the keys are
+     * defined. For example, {@code get("libdefaults", "forwardable")} will
+     * return null if "forwardable" is not defined in [libdefaults], and
+     * {@code get("realms", "R", "kdc")} will return null if "R" is not
+     * defined in [realms] or "kdc" is not defined for "R".
+     * @throws IllegalArgumentException if any of the keys is illegal, either
+     * because a key not the last one is not a (sub)section name or the last
+     * key is still a section name. For example, {@code get("libdefaults")}
+     * throws this exception because [libdefaults] is a section name instead of
+     * a value name, and {@code get("libdefaults", "forwardable", "tail")}
+     * also throws this exception because "forwardable" is already a value name
+     * and has no sub-key at all (given "forwardable" is defined, otherwise,
+     * this method has no knowledge if it's a value name or a section name),
+     */
+    @SuppressWarnings("unchecked")
+    public String get(String... keys) {
+        Vector<String> v = get0(keys);
+        if (v == null) return null;
+        return v.lastElement();
+    }
+
+    /**
+     * Gets all values for the specified keys.
+     * @see #get(java.lang.String[])
      */
-    public int getDefaultIntValue(String name) {
-        String result = null;
+    public String getAll(String... keys) {
+        Vector<String> v = get0(keys);
+        if (v == null) return null;
+        StringBuilder sb = new StringBuilder();
+        boolean first = true;
+        for (String s: v) {
+            if (first) {
+                sb.append(s);
+                first = false;
+            } else {
+                sb.append(' ').append(s);
+            }
+        }
+        return sb.toString();
+    }
+
+    // Internal method. Returns the vector of strings for keys.
+    // The only method (except for toString) that reads stanzaTable directly.
+    @SuppressWarnings("unchecked")
+    private Vector<String> get0(String... keys) {
+        Object current = stanzaTable;
+        try {
+            for (String key: keys) {
+                current = ((Hashtable<String,Object>)current).get(key);
+                if (current == null) return null;
+            }
+            return (Vector<String>)current;
+        } catch (ClassCastException cce) {
+            throw new IllegalArgumentException(cce);
+        }
+    }
+
+    /**
+     * Gets the int value for the specified keys.
+     * @param keys the keys
+     * @return the int value, Integer.MIN_VALUE is returned if it cannot be
+     * found or the value is not a legal integer.
+     * @throw IllegalArgumentException if any of the keys is illegal
+     * @see #get(java.lang.String[])
+     */
+    public int getIntValue(String... keys) {
+        String result = get(keys);
         int value = Integer.MIN_VALUE;
-        result = getDefault(name);
         if (result != null) {
             try {
                 value = parseIntValue(result);
             } catch (NumberFormatException e) {
                 if (DEBUG) {
                     System.out.println("Exception in getting value of " +
-                                       name + " " +
+                                       Arrays.toString(keys) + " " +
                                        e.getMessage());
-                    System.out.println("Setting " + name +
-                                       " to minimum value");
-                }
-                value = Integer.MIN_VALUE;
-            }
-        }
-        return value;
-    }
-
-    /**
-     * Gets the default int value for the specified name in the specified
-     * section. <br>This method is quicker by using section name as the
-     * search key.
-     * @param name the name.
-     * @param sectio the name string of the section.
-     * @return the default Integer, null is returned if no such name and
-     * value are found in configuration file, or error occurs when parsing
-     * string to integer.
-     */
-    public int getDefaultIntValue(String name, String section) {
-        String result = null;
-        int value = Integer.MIN_VALUE;
-        result = getDefault(name, section);
-        if (result != null) {
-            try {
-                value = parseIntValue(result);
-            } catch (NumberFormatException e) {
-                if (DEBUG) {
-                    System.out.println("Exception in getting value of " +
-                                       name +" in section " +
-                                       section + " "  + e.getMessage());
-                    System.out.println("Setting " + name +
+                    System.out.println("Setting " + Arrays.toString(keys) +
                                        " to minimum value");
                 }
                 value = Integer.MIN_VALUE;
@@ -258,152 +297,15 @@
     }
 
     /**
-     * Gets the default string value for the specified name.
-     * @param name the name.
-     * @return the default value, null is returned if it cannot be found.
-     */
-    public String getDefault(String name) {
-        if (stanzaTable == null) {
-            return null;
-        } else {
-            return getDefault(name, stanzaTable);
-        }
-    }
-
-    /**
-     * This method does the real job to recursively search through the
-     * stanzaTable.
-     * @param k the key string.
-     * @param t stanzaTable or sub hashtable within it.
-     * @return the value found in config file, returns null if no value
-     * matched with the key is found.
-     */
-    private String getDefault(String k, Hashtable<String, Object> t) {
-        String result = null;
-        String key;
-        if (stanzaTable != null) {
-            for (Enumeration<String> e = t.keys(); e.hasMoreElements(); ) {
-                key = e.nextElement();
-                Object ob = t.get(key);
-                if (ob instanceof Hashtable) {
-                    @SuppressWarnings("unchecked") // Checked with an instanceof check
-                    Hashtable<String, Object> table =
-                            (Hashtable<String, Object>)ob;
-                    result = getDefault(k, table);
-                    if (result != null) {
-                        return result;
-                    }
-                } else if (key.equalsIgnoreCase(k)) {
-                    if (ob instanceof String) {
-                        return (String)(t.get(key));
-                    } else if (ob instanceof Vector) {
-                        result = "";
-                        int length = ((Vector)ob).size();
-                        for (int i = 0; i < length; i++) {
-                            if (i == length -1) {
-                                result +=
-                                    (String)(((Vector)ob).elementAt(i));
-                            } else {
-                                result +=
-                                    (String)(((Vector)ob).elementAt(i)) + " ";
-                            }
-                        }
-                        return result;
-                    }
-                }
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Gets the default string value for the specified name in the
-     * specified section.
-     * <br>This method is quicker by using the section name as the search key.
-     * @param name the name.
-     * @param section the name of the section.
-     * @return the default value, null is returned if it cannot be found.
+     * Gets the boolean value for the specified keys.
+     * @param keys the keys
+     * @return the boolean value, false is returned if it cannot be
+     * found or the value is not "true" (case insensitive).
+     * @throw IllegalArgumentException if any of the keys is illegal
+     * @see #get(java.lang.String[])
      */
-    // stanzaTable leads to a lot of unchecked casts since its value type is
-    // STANZATABLE = String | Hashtable<String, STANZATABLE>
-    @SuppressWarnings("unchecked")
-    public String getDefault(String name, String section) {
-        String stanzaName;
-        String result = null;
-        Hashtable<String, Object> subTable;
-
-        if (stanzaTable != null) {
-            for (Enumeration<String> e = stanzaTable.keys();
-                 e.hasMoreElements(); ) {
-                stanzaName = e.nextElement();
-                subTable = (Hashtable<String, Object>)
-                        stanzaTable.get(stanzaName);
-                if (stanzaName.equalsIgnoreCase(section)) {
-                    if (subTable.containsKey(name)) {
-                        return (String)(subTable.get(name));
-                    }
-                } else if (subTable.containsKey(section)) {
-                    Object ob = subTable.get(section);
-                    if (ob instanceof Hashtable) {
-                        Hashtable<String, Object> temp =
-                                (Hashtable<String, Object>)ob;
-                        if (temp.containsKey(name)) {
-                            Object object = temp.get(name);
-                            if (object instanceof Vector) {
-                                result = "";
-                                int length = ((Vector)object).size();
-                                for (int i = 0; i < length; i++) {
-                                    if (i == length - 1)  {
-                                        result +=
-                                        (String)(((Vector)object).elementAt(i));
-                                    } else {
-                                        result +=
-                                        (String)(((Vector)object).elementAt(i))
-                                                + " ";
-                                    }
-                                }
-                            } else {
-                                result = (String)object;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Gets the default boolean value for the specified name.
-     * @param name the name.
-     * @return the default boolean value, false is returned if it cannot be
-     * found.
-     */
-    public boolean getDefaultBooleanValue(String name) {
-        String val = null;
-        if (stanzaTable == null) {
-            val = null;
-        } else {
-            val = getDefault(name, stanzaTable);
-        }
-        if (val != null && val.equalsIgnoreCase("true")) {
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Gets the default boolean value for the specified name in the
-     * specified section.
-     * <br>This method is quicker by using the section name as the search key.
-     * @param name the name.
-     * @param section the name of the section.
-     * @return the default boolean value, false is returned if it cannot be
-     * found.
-     */
-    public boolean getDefaultBooleanValue(String name, String section) {
-        String val = getDefault(name, section);
+    public boolean getBooleanValue(String... keys) {
+        String val = get(keys);
         if (val != null && val.equalsIgnoreCase("true")) {
             return true;
         } else {
@@ -528,29 +430,14 @@
     }
 
     /**
-     * Finds the matching value in the hashtable.
-     */
-    private String find(String key1, String key2) {
-        String result;
-        if ((stanzaTable != null) &&
-            ((result = (String)
-                (((Hashtable)(stanzaTable.get(key1))).get(key2))) != null)) {
-            return result;
-        } else {
-            return "";
-        }
-    }
-
-    /**
-     * Reads name/value pairs to the memory from the configuration
-     * file. The default location of the configuration file is in java home
-     * directory.
+     * Reads lines to the memory from the configuration file.
      *
      * Configuration file contains information about the default realm,
      * ticket parameters, location of the KDC and the admin server for
      * known realms, etc. The file is divided into sections. Each section
      * contains one or more name/value pairs with one pair per line. A
      * typical file would be:
+     * <pre>
      * [libdefaults]
      *          default_realm = EXAMPLE.COM
      *          default_tgs_enctypes = des-cbc-md5
@@ -568,128 +455,178 @@
      * [domain_realm]
      *          blue.sample.com = TEST.SAMPLE.COM
      *          .backup.com     = EXAMPLE.COM
-     *
-     * @params fileName the conf file, cannot be null
-     * @return the content, null if fileName is empty
-     * @throws IOException if there is an I/O or format error
+     * </pre>
+     * @return an ordered list of strings representing the config file after
+     * some initial processing, including:<ol>
+     * <li> Comment lines and empty lines are removed
+     * <li> "{" not at the end of a line is appended to the previous line
+     * <li> The content of a section is also placed between "{" and "}".
+     * <li> Lines are trimmed</ol>
+     * @throws IOException if there is an I/O error
+     * @throws KrbException if there is a file format error
      */
-    private Vector<String> loadConfigFile(final String fileName) throws IOException {
+    private List<String> loadConfigFile(final String fileName)
+            throws IOException, KrbException {
         try {
-            if (!fileName.equals("")) {
-                BufferedReader br = new BufferedReader(new InputStreamReader(
-                java.security.AccessController.doPrivileged(
-                new java.security.PrivilegedExceptionAction<FileInputStream> () {
-                public FileInputStream run() throws IOException {
-                    return new FileInputStream(fileName);
-                }
-                })));
-                String Line;
-                Vector<String> v = new Vector<>();
+            List<String> v = new ArrayList<>();
+            try (BufferedReader br = new BufferedReader(new InputStreamReader(
+                AccessController.doPrivileged(
+                    new PrivilegedExceptionAction<FileInputStream> () {
+                        public FileInputStream run() throws IOException {
+                            return new FileInputStream(fileName);
+                        }
+                    })))) {
+                String line;
                 String previous = null;
-                while ((Line = br.readLine()) != null) {
-                    // ignore comments and blank line in the configuration file.
-                    // Comments start with #.
-                    if (!(Line.startsWith("#") || Line.trim().isEmpty())) {
-                        String current = Line.trim();
-                        // In practice, a subsection might look like:
-                        //      EXAMPLE.COM =
-                        //      {
-                        //              kdc = kerberos.example.com
-                        //              ...
-                        //      }
-                        // Before parsed into stanza table, it needs to be
-                        // converted into formal style:
-                        //      EXAMPLE.COM = {
-                        //              kdc = kerberos.example.com
-                        //              ...
-                        //      }
-                        //
-                        // So, if a line is "{", adhere to the previous line.
-                        if (current.equals("{")) {
-                            if (previous == null) {
-                                throw new IOException(
-                                    "Config file should not start with \"{\"");
-                            }
-                            previous += " " + current;
-                        } else {
-                            if (previous != null) {
-                                v.addElement(previous);
-                            }
-                            previous = current;
+                while ((line = br.readLine()) != null) {
+                    line = line.trim();
+                    if (line.startsWith("#") || line.isEmpty()) {
+                        // ignore comments and blank line
+                        // Comments start with #.
+                        continue;
+                    }
+                    // In practice, a subsection might look like:
+                    //      [realms]
+                    //      EXAMPLE.COM =
+                    //      {
+                    //          kdc = kerberos.example.com
+                    //          ...
+                    //      }
+                    // Before parsed into stanza table, it needs to be
+                    // converted into a canonicalized style (no indent):
+                    //      realms = {
+                    //          EXAMPLE.COM = {
+                    //              kdc = kerberos.example.com
+                    //              ...
+                    //          }
+                    //      }
+                    //
+                    if (line.startsWith("[")) {
+                        if (!line.endsWith("]")) {
+                            throw new KrbException("Illegal config content:"
+                                    + line);
                         }
+                        if (previous != null) {
+                            v.add(previous);
+                            v.add("}");
+                        }
+                        String title = line.substring(
+                                1, line.length()-1).trim();
+                        if (title.isEmpty()) {
+                            throw new KrbException("Illegal config content:"
+                                    + line);
+                        }
+                        previous = title + " = {";
+                    } else if (line.startsWith("{")) {
+                        if (previous == null) {
+                            throw new KrbException(
+                                "Config file should not start with \"{\"");
+                        }
+                        previous += " {";
+                        if (line.length() > 1) {
+                            // { and content on the same line
+                            v.add(previous);
+                            previous = line.substring(1).trim();
+                        }
+                    } else {
+                        if (previous == null) {
+                            throw new KrbException(
+                                "Config file must starts with a section");
+                        }
+                        v.add(previous);
+                        previous = line;
                     }
                 }
                 if (previous != null) {
-                    v.addElement(previous);
+                    v.add(previous);
+                    v.add("}");
                 }
-
-                br.close();
-                return v;
             }
-            return null;
+            return v;
         } catch (java.security.PrivilegedActionException pe) {
             throw (IOException)pe.getException();
         }
     }
 
-
     /**
      * Parses stanza names and values from configuration file to
      * stanzaTable (Hashtable). Hashtable key would be stanza names,
      * (libdefaults, realms, domain_realms, etc), and the hashtable value
      * would be another hashtable which contains the key-value pairs under
-     * a stanza name.
+     * a stanza name. The value of this sub-hashtable can be another hashtable
+     * containing another sub-sub-section or a vector of strings for
+     * final values (even if there is only one value defined).
+     * <p>
+     * For duplicates section names, the latter overwrites the former. For
+     * duplicate value names, the values are in a vector in its appearing order.
+     * </ol>
+     * Please note that this behavior is Java traditional. and it is
+     * not the same as the MIT krb5 behavior, where:<ol>
+     * <li>Duplicated root sections will be merged
+     * <li>For duplicated sub-sections, the former overwrites the latter
+     * <li>Duplicate keys for values are always saved in a vector
+     * </ol>
+     * @param v the strings in the file, never null, might be empty
+     * @throws KrbException if there is a file format error
      */
-    private Hashtable<String,Object> parseStanzaTable(Vector<String> v) throws KrbException {
-        if (v == null) {
-            throw new KrbException("I/O error while reading" +
-                        " configuration file.");
-        }
-        Hashtable<String,Object> table = new Hashtable<>();
-        for (int i = 0; i < v.size(); i++) {
-            String line = v.elementAt(i).trim();
-            if (line.equalsIgnoreCase("[realms]")) {
-                for (int count = i + 1; count < v.size() + 1; count++) {
-                    // find the next stanza name
-                    if ((count == v.size()) ||
-                        (v.elementAt(count).startsWith("["))) {
-                        Hashtable<String,Hashtable<String,Vector<String>>> temp =
-                            new Hashtable<>();
-                        temp = parseRealmField(v, i + 1, count);
-                        table.put("realms", temp);
-                        i = count - 1;
-                        break;
-                    }
+    @SuppressWarnings("unchecked")
+    private Hashtable<String,Object> parseStanzaTable(List<String> v)
+            throws KrbException {
+        Hashtable<String,Object> current = stanzaTable;
+        for (String line: v) {
+            // There are 3 kinds of lines
+            // 1. a = b
+            // 2. a = {
+            // 3. }
+            if (line.equals("}")) {
+                // Go back to parent, see below
+                current = (Hashtable<String,Object>)current.remove(" PARENT ");
+                if (current == null) {
+                    throw new KrbException("Unmatched close brace");
                 }
-            } else if (line.equalsIgnoreCase("[capaths]")) {
-                for (int count = i + 1; count < v.size() + 1; count++) {
-                    // find the next stanza name
-                    if ((count == v.size()) ||
-                        (v.elementAt(count).startsWith("["))) {
-                        Hashtable<String,Hashtable<String,Vector<String>>> temp =
-                            new Hashtable<>();
-                        temp = parseRealmField(v, i + 1, count);
-                        table.put("capaths", temp);
-                        i = count - 1;
-                        break;
-                    }
+            } else {
+                int pos = line.indexOf('=');
+                if (pos < 0) {
+                    throw new KrbException("Illegal config content:" + line);
                 }
-            } else if (line.startsWith("[") && line.endsWith("]")) {
-                String key = line.substring(1, line.length() - 1);
-                for (int count = i + 1; count < v.size() + 1; count++) {
-                    // find the next stanza name
-                    if ((count == v.size()) ||
-                        (v.elementAt(count).startsWith("["))) {
-                        Hashtable<String,String> temp =
-                            parseField(v, i + 1, count);
-                        table.put(key, temp);
-                        i = count - 1;
-                        break;
+                String key = line.substring(0, pos).trim();
+                String value = trimmed(line.substring(pos+1));
+                if (value.equals("{")) {
+                    Hashtable<String,Object> subTable;
+                    if (current == stanzaTable) {
+                        key = key.toLowerCase(Locale.US);
                     }
+                    subTable = new Hashtable<>();
+                    current.put(key, subTable);
+                    // A special entry for its parent. Put whitespaces around,
+                    // so will never be confused with a normal key
+                    subTable.put(" PARENT ", current);
+                    current = subTable;
+                } else {
+                    Vector<String> values;
+                    if (current.containsKey(key)) {
+                        Object obj = current.get(key);
+                        // If a key first shows as a section and then a value,
+                        // this is illegal. However, we haven't really forbid
+                        // first value then section, which the final result
+                        // is a section.
+                        if (!(obj instanceof Vector)) {
+                            throw new KrbException("Key " + key
+                                    + "used for both value and section");
+                        }
+                        values = (Vector<String>)current.get(key);
+                    } else {
+                        values = new Vector<String>();
+                        current.put(key, values);
+                    }
+                    values.add(value);
                 }
             }
         }
-        return table;
+        if (current != stanzaTable) {
+            throw new KrbException("Not closed");
+        }
+        return current;
     }
 
     /**
@@ -807,158 +744,20 @@
 
     private static String trimmed(String s) {
         s = s.trim();
+        if (s.isEmpty()) return s;
         if (s.charAt(0) == '"' && s.charAt(s.length()-1) == '"' ||
                 s.charAt(0) == '\'' && s.charAt(s.length()-1) == '\'') {
             s = s.substring(1, s.length()-1).trim();
         }
         return s;
     }
-    /**
-     * Parses key-value pairs under a stanza name.
-     */
-    private Hashtable<String,String>  parseField(Vector<String> v, int start, int end) {
-        Hashtable<String,String> table = new Hashtable<>();
-        String line;
-        for (int i = start; i < end; i++) {
-            line = v.elementAt(i);
-            for (int j = 0; j < line.length(); j++) {
-                if (line.charAt(j) == '=') {
-                    String key = (line.substring(0, j)).trim();
-                    String value = trimmed(line.substring(j + 1));
-                    table.put(key, value);
-                    break;
-                }
-            }
-        }
-        return table;
-    }
-
-    /**
-     * Parses key-value pairs under [realms].  The key would be the realm
-     * name, the value would be another hashtable which contains
-     * information for the realm given within a pair of braces.
-     */
-    private Hashtable<String,Hashtable<String,Vector<String>>> parseRealmField(Vector<String> v, int start, int end) {
-        Hashtable<String,Hashtable<String,Vector<String>>> table = new Hashtable<>();
-        String line;
-        for (int i = start; i < end; i++) {
-            line = v.elementAt(i).trim();
-            if (line.endsWith("{")) {
-                String key = "";
-                for (int j = 0; j < line.length(); j++) {
-                    if (line.charAt(j) == '=') {
-                        key = line.substring(0, j).trim();
-                        // get the key
-                        break;
-                    }
-                }
-                for (int k = i + 1; k < end; k++) {
-                    boolean found = false;
-                    line = v.elementAt(k).trim();
-                    for (int l = 0; l < line.length(); l++) {
-                        if (line.charAt(l) == '}') {
-                            found = true;
-                            break;
-                        }
-                    }
-                    if (found == true) {
-                        Hashtable<String,Vector<String>> temp = parseRealmFieldEx(v, i + 1, k);
-                        table.put(key, temp);
-                        i = k;
-                        found = false;
-                        break;
-                    }
-
-                }
-            }
-        }
-        return table;
-    }
-
-    /**
-     * Parses key-value pairs within each braces under [realms].
-     */
-    private Hashtable<String,Vector<String>> parseRealmFieldEx(Vector<String> v, int start, int end) {
-        Hashtable<String,Vector<String>> table = new Hashtable<>();
-        Vector<String> keyVector = new Vector<>();
-        Vector<String> nameVector = new Vector<>();
-        String line = "";
-        String key;
-        for (int i = start; i < end; i++) {
-            line = v.elementAt(i);
-            for (int j = 0; j < line.length(); j++) {
-                if (line.charAt(j) == '=') {
-                    int index;
-                    key = line.substring(0, j).trim();
-                    if (! exists(key, keyVector)) {
-                        keyVector.addElement(key);
-                        nameVector = new Vector<String> ();
-                    } else {
-                        nameVector = table.get(key);
-                    }
-                    nameVector.addElement(trimmed(line.substring(j + 1)));
-                    table.put(key, nameVector);
-                    break;
-                }
-            }
-        }
-        return table;
-    }
-
-    /**
-     * Compares the key with the known keys to see if it exists.
-     */
-    private boolean exists(String key, Vector<String> v) {
-        boolean exists = false;
-        for (int i = 0; i < v.size(); i++) {
-            if (v.elementAt(i).equals(key)) {
-                exists = true;
-            }
-        }
-        return exists;
-    }
 
     /**
      * For testing purpose. This method lists all information being parsed from
      * the configuration file to the hashtable.
      */
     public void listTable() {
-        listTable(stanzaTable);
-    }
-
-    // stanzaTable leads to a lot of unchecked casts since its value type is
-    // STANZATABLE = String | Hashtable<String, STANZATABLE>
-    @SuppressWarnings("unchecked")
-    private void listTable(Hashtable<String, Object> table) {
-        Vector<String> v = new Vector<String>();
-        String key;
-        if (stanzaTable != null) {
-            for (Enumeration<String> e = table.keys(); e.hasMoreElements(); ) {
-                key = e.nextElement();
-                Object object = table.get(key);
-                if (table == stanzaTable) {
-                    System.out.println("[" + key + "]");
-                }
-                if (object instanceof Hashtable) {
-                    if (table != stanzaTable)
-                        System.out.println("\t" + key + " = {");
-                    listTable((Hashtable<String, Object>)object);
-                    if (table != stanzaTable)
-                        System.out.println("\t}");
-
-                } else if (object instanceof String) {
-                    System.out.println("\t" + key + " = " +
-                                (String)table.get(key));
-                } else if (object instanceof Vector) {
-                    v = (Vector<String>)object;
-                    for (int i = 0; i < v.size(); i++) {
-                        System.out.println("\t" + key + " = " + v.elementAt(i));
-                    }
-                }
-            }
-        } else {
-            System.out.println("Configuration file not found.");
-        }
+        System.out.println(this);
     }
 
     /**
@@ -967,7 +766,7 @@
      */
     public int[] defaultEtype(String enctypes) {
         String default_enctypes;
-        default_enctypes = getDefault(enctypes, "libdefaults");
+        default_enctypes = get("libdefaults", enctypes);
         String delim = " ";
         StringTokenizer st;
         int[] etype;
@@ -991,7 +790,7 @@
             ArrayList<Integer> ls = new ArrayList<>(len);
             int type;
             for (int i = 0; i < len; i++) {
-                type = getType(st.nextToken());
+                type = Config.getType(st.nextToken());
                 if ((type != -1) &&
                     (EType.isSupported(type))) {
                     ls.add(type);
@@ -1032,7 +831,7 @@
      * checksum type to int value that can be later used by EType and
      * Checksum classes.
      */
-    public int getType(String input) {
+    public static int getType(String input) {
         int result = -1;
         if (input == null) {
             return result;
@@ -1114,11 +913,11 @@
     public boolean useAddresses() {
         boolean useAddr = false;
         // use addresses if "no_addresses" is set to false
-        String value = getDefault("no_addresses", "libdefaults");
+        String value = get("libdefaults", "no_addresses");
         useAddr = (value != null && value.equalsIgnoreCase("false"));
         if (useAddr == false) {
             // use addresses if "noaddresses" is set to false
-            value = getDefault("noaddresses", "libdefaults");
+            value = get("libdefaults", "noaddresses");
             useAddr = (value != null && value.equalsIgnoreCase("false"));
         }
         return useAddr;
@@ -1127,10 +926,10 @@
     /**
      * Check if need to use DNS to locate Kerberos services
      */
-    public boolean useDNS(String name) {
-        String value = getDefault(name, "libdefaults");
+    private boolean useDNS(String name) {
+        String value = get("libdefaults", name);
         if (value == null) {
-            value = getDefault("dns_fallback", "libdefaults");
+            value = get("libdefaults", "dns_fallback");
             if ("false".equalsIgnoreCase(value)) {
                 return false;
             } else {
@@ -1144,14 +943,14 @@
     /**
      * Check if need to use DNS to locate the KDC
      */
-    public boolean useDNS_KDC() {
+    private boolean useDNS_KDC() {
         return useDNS("dns_lookup_kdc");
     }
 
     /*
      * Check if need to use DNS to locate the Realm
      */
-    public boolean useDNS_Realm() {
+    private boolean useDNS_Realm() {
         return useDNS("dns_lookup_realm");
     }
 
@@ -1165,7 +964,7 @@
             return defaultRealm;
         }
         Exception cause = null;
-        String realm = getDefault("default_realm", "libdefaults");
+        String realm = get("libdefaults", "default_realm");
         if ((realm == null) && useDNS_Realm()) {
             // use DNS to locate Kerberos realm
             try {
@@ -1212,7 +1011,7 @@
             return defaultKDC;
         }
         Exception cause = null;
-        String kdcs = getDefault("kdc", realm);
+        String kdcs = getAll("realms", realm, "kdc");
         if ((kdcs == null) && useDNS_KDC()) {
             // use DNS to locate KDC
             try {
--- a/src/share/classes/sun/security/krb5/KdcComm.java	Sat Oct 27 09:18:29 2012 +0100
+++ b/src/share/classes/sun/security/krb5/KdcComm.java	Mon Oct 29 14:14:06 2012 +0800
@@ -142,11 +142,11 @@
 
         try {
             Config cfg = Config.getInstance();
-            String temp = cfg.getDefault("kdc_timeout", "libdefaults");
+            String temp = cfg.get("libdefaults", "kdc_timeout");
             timeout = parsePositiveIntString(temp);
-            temp = cfg.getDefault("max_retries", "libdefaults");
+            temp = cfg.get("libdefaults", "max_retries");
             max_retries = parsePositiveIntString(temp);
-            temp = cfg.getDefault("udp_preference_limit", "libdefaults");
+            temp = cfg.get("libdefaults", "udp_preference_limit");
             udf_pref_limit = parsePositiveIntString(temp);
         } catch (Exception exc) {
            // ignore any exceptions; use default values
@@ -421,7 +421,7 @@
         int temp = -1;
         try {
             String value =
-               Config.getInstance().getDefault(key, realm);
+               Config.getInstance().get("realms", realm, key);
             temp = parsePositiveIntString(value);
         } catch (Exception exc) {
             // Ignored, defValue will be picked up
--- a/src/share/classes/sun/security/krb5/PrincipalName.java	Sat Oct 27 09:18:29 2012 +0100
+++ b/src/share/classes/sun/security/krb5/PrincipalName.java	Mon Oct 29 14:14:06 2012 +0800
@@ -655,19 +655,19 @@
         try {
             String subname = null;
             Config c = Config.getInstance();
-            if ((result = c.getDefault(name, "domain_realm")) != null)
+            if ((result = c.get("domain_realm", name)) != null)
                 return result;
             else {
                 for (int i = 1; i < name.length(); i++) {
                     if ((name.charAt(i) == '.') && (i != name.length() - 1)) { //mapping could be .ibm.com = AUSTIN.IBM.COM
                         subname = name.substring(i);
-                        result = c.getDefault(subname, "domain_realm");
+                        result = c.get("domain_realm", subname);
                         if (result != null) {
                             break;
                         }
                         else {
                             subname = name.substring(i + 1);      //or mapping could be ibm.com = AUSTIN.IBM.COM
-                            result = c.getDefault(subname, "domain_realm");
+                            result = c.get("domain_realm", subname);
                             if (result != null) {
                                 break;
                             }
--- a/src/share/classes/sun/security/krb5/Realm.java	Sat Oct 27 09:18:29 2012 +0100
+++ b/src/share/classes/sun/security/krb5/Realm.java	Mon Oct 29 14:14:06 2012 +0800
@@ -350,7 +350,7 @@
             return null;
         }
 
-        String intermediaries = cfg.getDefault(sRealm, cRealm);
+        String intermediaries = cfg.getAll("capaths", cRealm, sRealm);
 
         if (intermediaries == null) {
             if (DEBUG) {
@@ -459,7 +459,7 @@
                                    tempTarget);
             }
 
-            intermediaries = cfg.getDefault(tempTarget, cRealm);
+            intermediaries = cfg.getAll("capaths", cRealm, tempTarget);
 
         } while (true);
 
--- a/src/share/classes/sun/security/krb5/SCDynamicStoreConfig.java	Sat Oct 27 09:18:29 2012 +0100
+++ b/src/share/classes/sun/security/krb5/SCDynamicStoreConfig.java	Mon Oct 29 14:14:06 2012 +0800
@@ -34,19 +34,25 @@
 public class SCDynamicStoreConfig {
     private static native void installNotificationCallback();
     private static native Hashtable<String, Object> getKerberosConfig();
+    private static boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG;
 
     static {
-        java.security.AccessController.doPrivileged(
-            new java.security.PrivilegedAction<Void>() {
-                public Void run() {
-                    System.loadLibrary("osx");
-                    return null;
+        boolean isMac = java.security.AccessController.doPrivileged(
+            new java.security.PrivilegedAction<Boolean>() {
+                public Boolean run() {
+                    String osname = System.getProperty("os.name");
+                    if (osname.contains("OS X")) {
+                        System.loadLibrary("osx");
+                        return true;
+                    }
+                    return false;
                 }
             });
-        installNotificationCallback();
+        if (isMac) installNotificationCallback();
     }
 
-    private static Vector<String> unwrapHost(Collection<Hashtable<String, String>> c) {
+    private static Vector<String> unwrapHost(
+            Collection<Hashtable<String, String>> c) {
         Vector<String> vector = new Vector<String>();
         for (Hashtable<String, String> m : c) {
             vector.add(m.get("host"));
@@ -60,20 +66,25 @@
      * are wrapped inside Hashtables
      */
     @SuppressWarnings("unchecked")
-    private static Hashtable<String, Object> convertRealmConfigs(Hashtable<String, ?> configs) {
+    private static Hashtable<String, Object>
+            convertRealmConfigs(Hashtable<String, ?> configs) {
         Hashtable<String, Object> realmsTable = new Hashtable<String, Object>();
 
         for (String realm : configs.keySet()) {
             // get the kdc
-            Hashtable<String, Collection<?>> map = (Hashtable<String, Collection<?>>) configs.get(realm);
-            Collection<Hashtable<String, String>> kdc = (Collection<Hashtable<String, String>>) map.get("kdc");
+            Hashtable<String, Collection<?>> map =
+                    (Hashtable<String, Collection<?>>) configs.get(realm);
+            Hashtable<String, Vector<String>> realmMap =
+                    new Hashtable<String, Vector<String>>();
 
             // put the kdc into the realmMap
-            Hashtable<String, Vector<String>> realmMap = new Hashtable<String, Vector<String>>();
+            Collection<Hashtable<String, String>> kdc =
+                    (Collection<Hashtable<String, String>>) map.get("kdc");
             if (kdc != null) realmMap.put("kdc", unwrapHost(kdc));
 
             // put the admin server into the realmMap
-            Collection<Hashtable<String, String>> kadmin = (Collection<Hashtable<String, String>>) map.get("kadmin");
+            Collection<Hashtable<String, String>> kadmin =
+                    (Collection<Hashtable<String, String>>) map.get("kadmin");
             if (kadmin != null) realmMap.put("admin_server", unwrapHost(kadmin));
 
             // add the full entry to the realmTable
@@ -90,23 +101,44 @@
      * @return
      * @throws IOException
      */
-    @SuppressWarnings("unchecked")
     public static Hashtable<String, Object> getConfig() throws IOException {
         Hashtable<String, Object> stanzaTable = getKerberosConfig();
         if (stanzaTable == null) {
-            throw new IOException("Could not load configuration from SCDynamicStore");
+            throw new IOException(
+                    "Could not load configuration from SCDynamicStore");
         }
-        //System.out.println("Raw map from JNI: " + stanzaTable);
+        if (DEBUG) System.out.println("Raw map from JNI: " + stanzaTable);
+        return convertNativeConfig(stanzaTable);
+    }
 
+    @SuppressWarnings("unchecked")
+    private static Hashtable<String, Object> convertNativeConfig(
+            Hashtable<String, Object> stanzaTable) {
         // convert SCDynamicStore realm structure to Java realm structure
-        Hashtable<String, ?> realms = (Hashtable<String, ?>) stanzaTable.get("realms");
+        Hashtable<String, ?> realms =
+                (Hashtable<String, ?>) stanzaTable.get("realms");
         if (realms != null) {
             stanzaTable.remove("realms");
             Hashtable<String, Object> realmsTable = convertRealmConfigs(realms);
             stanzaTable.put("realms", realmsTable);
         }
-
-       // System.out.println("stanzaTable : " + stanzaTable);
+        WrapAllStringInVector(stanzaTable);
+        if (DEBUG) System.out.println("stanzaTable : " + stanzaTable);
         return stanzaTable;
     }
+
+    @SuppressWarnings("unchecked")
+    private static void WrapAllStringInVector(
+            Hashtable<String, Object> stanzaTable) {
+        for (String s: stanzaTable.keySet()) {
+            Object v = stanzaTable.get(s);
+            if (v instanceof Hashtable) {
+                WrapAllStringInVector((Hashtable<String,Object>)v);
+            } else if (v instanceof String) {
+                Vector<String> vec = new Vector<>();
+                vec.add((String)v);
+                stanzaTable.put(s, vec);
+            }
+        }
+    }
 }
--- a/src/share/classes/sun/security/krb5/internal/KDCOptions.java	Sat Oct 27 09:18:29 2012 +0100
+++ b/src/share/classes/sun/security/krb5/internal/KDCOptions.java	Mon Oct 29 14:14:06 2012 +0800
@@ -244,25 +244,23 @@
 
             Config config = Config.getInstance();
 
-            /*
-             * First see if the IBM hex format is being used.
-             * If not, try the Sun's string (boolean) format.
-             */
+            // If key not present, returns Integer.MIN_VALUE, which is
+            // almost all zero.
 
-            int options =config.getDefaultIntValue("kdc_default_options",
-                    "libdefaults");
+            int options = config.getIntValue("libdefaults",
+                    "kdc_default_options");
 
             if ((options & RENEWABLE_OK) == RENEWABLE_OK) {
                 set(RENEWABLE_OK, true);
             } else {
-                if (config.getDefaultBooleanValue("renewable", "libdefaults")) {
+                if (config.getBooleanValue("libdefaults", "renewable")) {
                     set(RENEWABLE_OK, true);
                 }
             }
             if ((options & PROXIABLE) == PROXIABLE) {
                 set(PROXIABLE, true);
             } else {
-                if (config.getDefaultBooleanValue("proxiable", "libdefaults")) {
+                if (config.getBooleanValue("libdefaults", "proxiable")) {
                     set(PROXIABLE, true);
                 }
             }
@@ -270,7 +268,7 @@
             if ((options & FORWARDABLE) == FORWARDABLE) {
                 set(FORWARDABLE, true);
             } else {
-                if (config.getDefaultBooleanValue("forwardable", "libdefaults")) {
+                if (config.getBooleanValue("libdefaults", "forwardable")) {
                     set(FORWARDABLE, true);
                 }
             }
--- a/src/share/classes/sun/security/krb5/internal/KerberosTime.java	Sat Oct 27 09:18:29 2012 +0100
+++ b/src/share/classes/sun/security/krb5/internal/KerberosTime.java	Mon Oct 29 14:14:06 2012 +0800
@@ -350,9 +350,9 @@
     public static int getDefaultSkew() {
         int tdiff = Krb5.DEFAULT_ALLOWABLE_CLOCKSKEW;
         try {
-            Config c = Config.getInstance();
-            if ((tdiff = c.getDefaultIntValue("clockskew",
-                                              "libdefaults")) == Integer.MIN_VALUE) {   //value is not defined
+            if ((tdiff = Config.getInstance().getIntValue(
+                    "libdefaults", "clockskew"))
+                        == Integer.MIN_VALUE) {   //value is not defined
                 tdiff = Krb5.DEFAULT_ALLOWABLE_CLOCKSKEW;
             }
         } catch (KrbException e) {
--- a/src/share/classes/sun/security/krb5/internal/crypto/CksumType.java	Sat Oct 27 09:18:29 2012 +0100
+++ b/src/share/classes/sun/security/krb5/internal/crypto/CksumType.java	Mon Oct 29 14:14:06 2012 +0800
@@ -126,10 +126,10 @@
         int cksumType = Checksum.CKSUMTYPE_RSA_MD5; // default
         try {
             Config c = Config.getInstance();
-            if ((cksumType = (c.getType(c.getDefault("ap_req_checksum_type",
-                                "libdefaults")))) == - 1) {
-                if ((cksumType = c.getType(c.getDefault("checksum_type",
-                                "libdefaults"))) == -1) {
+            if ((cksumType = (Config.getType(c.get("libdefaults",
+                    "ap_req_checksum_type")))) == - 1) {
+                if ((cksumType = Config.getType(c.get("libdefaults",
+                        "checksum_type"))) == -1) {
                     cksumType = Checksum.CKSUMTYPE_RSA_MD5; // default
                 }
             }
--- a/src/share/classes/sun/security/krb5/internal/crypto/EType.java	Sat Oct 27 09:18:29 2012 +0100
+++ b/src/share/classes/sun/security/krb5/internal/crypto/EType.java	Mon Oct 29 14:14:06 2012 +0800
@@ -48,13 +48,17 @@
 public abstract class EType {
 
     private static final boolean DEBUG = Krb5.DEBUG;
-    private static final boolean ALLOW_WEAK_CRYPTO;
+    private static boolean allowWeakCrypto;
 
     static {
+        initStatic();
+    }
+
+    public static void initStatic() {
         boolean allowed = true;
         try {
             Config cfg = Config.getInstance();
-            String temp = cfg.getDefault("allow_weak_crypto", "libdefaults");
+            String temp = cfg.get("libdefaults", "allow_weak_crypto");
             if (temp != null && temp.equals("false")) allowed = false;
         } catch (Exception exc) {
             if (DEBUG) {
@@ -63,7 +67,7 @@
                                     exc.getMessage());
             }
         }
-        ALLOW_WEAK_CRYPTO = allowed;
+        allowWeakCrypto = allowed;
     }
 
     public static EType getInstance  (int eTypeConst)
@@ -216,7 +220,7 @@
         } else {
             result = BUILTIN_ETYPES;
         }
-        if (!ALLOW_WEAK_CRYPTO) {
+        if (!allowWeakCrypto) {
             // The last 2 etypes are now weak ones
             return Arrays.copyOfRange(result, 0, result.length - 2);
         }
--- a/src/share/classes/sun/security/krb5/internal/ktab/KeyTab.java	Sat Oct 27 09:18:29 2012 +0100
+++ b/src/share/classes/sun/security/krb5/internal/ktab/KeyTab.java	Mon Oct 29 14:14:06 2012 +0800
@@ -186,8 +186,8 @@
         } else {
             String kname = null;
             try {
-                String keytab_names = Config.getInstance().getDefault
-                    ("default_keytab_name", "libdefaults");
+                String keytab_names = Config.getInstance().get
+                        ("libdefaults", "default_keytab_name");
                 if (keytab_names != null) {
                     StringTokenizer st = new StringTokenizer(keytab_names, " ");
                     while (st.hasMoreTokens()) {
--- a/test/sun/security/krb5/ConfPlusProp.java	Sat Oct 27 09:18:29 2012 +0100
+++ b/test/sun/security/krb5/ConfPlusProp.java	Mon Oct 29 14:14:06 2012 +0800
@@ -25,6 +25,7 @@
  * @bug 6857795
  * @bug 6858589
  * @bug 6972005
+ * @compile -XDignore.symbol.file ConfPlusProp.java
  * @run main/othervm ConfPlusProp
  * @summary krb5.conf ignored if system properties on realm and kdc are provided
  */
@@ -75,7 +76,7 @@
         check("R1", "k1");
         check("R2", "old");
         check("R3", null);
-        if (!config.getDefault("forwardable", "libdefaults").equals("well")) {
+        if (!config.get("libdefaults", "forwardable").equals("well")) {
             throw new Exception("Extra config error");
         }
 
@@ -103,7 +104,7 @@
             check("R1", null);
             check("R2", null);
             check("R3", null);
-            if (config.getDefault("forwardable", "libdefaults") != null) {
+            if (config.get("libdefaults", "forwardable") != null) {
                 throw new Exception("Extra config error");
             }
         }
@@ -121,7 +122,7 @@
         check("R1", "k1");
         check("R2", "k2");
         check("R3", "k2");
-        if (!config.getDefault("forwardable", "libdefaults").equals("well")) {
+        if (!config.get("libdefaults", "forwardable").equals("well")) {
             throw new Exception("Extra config error");
         }
 
@@ -143,7 +144,7 @@
         check("R1", "k2");
         check("R2", "k2");
         check("R3", "k2");
-        if (config.getDefault("forwardable", "libdefaults") != null) {
+        if (config.get("libdefaults", "forwardable") != null) {
             throw new Exception("Extra config error");
         }
     }
--- a/test/sun/security/krb5/DnsFallback.java	Sat Oct 27 09:18:29 2012 +0100
+++ b/test/sun/security/krb5/DnsFallback.java	Mon Oct 29 14:14:06 2012 +0800
@@ -28,12 +28,20 @@
  * @summary fix dns_fallback parse error, and use dns by default
  */
 
-import sun.security.krb5.*;
 import java.io.*;
+import java.lang.reflect.Method;
+import sun.security.krb5.Config;
 
 public class DnsFallback {
+
+    static Method useDNS_Realm;
+
     public static void main(String[] args) throws Exception {
 
+        useDNS_Realm = Config.class.getDeclaredMethod("useDNS_Realm");
+        useDNS_Realm.setAccessible(true);
+
+
         // for 6673164
         check("true", "true", true);
         check("false", "true", false);
@@ -48,22 +56,25 @@
         check(null, null, true);
     }
 
-    static void check(String realm, String fallback, boolean output) throws Exception {
-        FileOutputStream fo = new FileOutputStream("dnsfallback.conf");
-        StringBuffer sb = new StringBuffer();
-        sb.append("[libdefaults]\n");
-        if (realm != null) {
-            sb.append("dns_lookup_realm=" + realm + "\n");
+    static void check(String realm, String fallback, boolean output)
+            throws Exception {
+
+        try (PrintStream ps =
+                new PrintStream(new FileOutputStream("dnsfallback.conf"))) {
+            ps.println("[libdefaults]\n");
+            if (realm != null) {
+                ps.println("dns_lookup_realm=" + realm);
+            }
+            if (fallback != null) {
+                ps.println("dns_fallback=" + fallback);
+            }
         }
-        if (fallback != null) {
-            sb.append("dns_fallback=" + fallback + "\n");
-        }
-        fo.write(sb.toString().getBytes());
-        fo.close();
+
         System.setProperty("java.security.krb5.conf", "dnsfallback.conf");
         Config.refresh();
         System.out.println("Testing " + realm + ", " + fallback + ", " + output);
-        if (Config.getInstance().useDNS_Realm() != output) {
+
+        if (!useDNS_Realm.invoke(Config.getInstance()).equals(output)) {
             throw new Exception("Fail");
         }
     }
--- a/test/sun/security/krb5/ParseConfig.java	Sat Oct 27 09:18:29 2012 +0100
+++ b/test/sun/security/krb5/ParseConfig.java	Mon Oct 29 14:14:06 2012 +0800
@@ -23,6 +23,7 @@
 /*
  * @test
  * @bug 6319046
+ * @compile -XDignore.symbol.file ParseConfig.java
  * @run main/othervm ParseConfig
  * @summary Problem with parsing krb5.conf
  */
@@ -37,7 +38,7 @@
 
         String sample = "kdc.example.com kdc2.example.com";
         for ( int i = 0; i < 4; i++ ) {
-            String expected = config.getDefault("kdc", "EXAMPLE_" + i + ".COM");
+            String expected = config.getAll("realms", "EXAMPLE_" + i + ".COM", "kdc");
             if (!sample.equals(expected)) {
                 throw new Exception("krb5.conf: unexpected kdc value \"" +
                         expected + "\"");
--- a/test/sun/security/krb5/auto/BasicKrb5Test.java	Sat Oct 27 09:18:29 2012 +0100
+++ b/test/sun/security/krb5/auto/BasicKrb5Test.java	Mon Oct 29 14:14:06 2012 +0800
@@ -25,6 +25,7 @@
  * @test
  * @bug 6706974
  * @summary Add krb5 test infrastructure
+ * @compile -XDignore.symbol.file BasicKrb5Test.java
  * @run main/othervm BasicKrb5Test
  * @run main/othervm BasicKrb5Test des-cbc-crc
  * @run main/othervm BasicKrb5Test des-cbc-md5
@@ -86,7 +87,7 @@
         new OneKDC(etype).writeJAASConf();
 
         System.out.println("Testing etype " + etype);
-        if (etype != null && !EType.isSupported(Config.getInstance().getType(etype))) {
+        if (etype != null && !EType.isSupported(Config.getType(etype))) {
             // aes256 is not enabled on all systems
             System.out.println("Not supported.");
             return;
--- a/test/sun/security/krb5/auto/MaxRetries.java	Sat Oct 27 09:18:29 2012 +0100
+++ b/test/sun/security/krb5/auto/MaxRetries.java	Mon Oct 29 14:14:06 2012 +0800
@@ -108,7 +108,7 @@
             if (line.startsWith(">>> KDCCommunication")) {
                 System.out.println(line);
                 if (line.indexOf(timeoutTag) < 0) {
-                    throw new Exception("Wrong timeout value");
+                    throw new Exception("Wrong timeout value" + timeoutTag);
                 }
                 count--;
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/krb5/config/Duplicates.java	Mon Oct 29 14:14:06 2012 +0800
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2012, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 7184246
+ * @compile -XDignore.symbol.file Duplicates.java
+ * @run main/othervm Duplicates
+ * @summary Simplify Config.get() of krb5
+ */
+
+import sun.security.krb5.Config;
+
+public class Duplicates {
+    public static void main(String[] args) throws Exception {
+        System.setProperty("java.security.krb5.conf",
+                System.getProperty("test.src", ".") +"/k1.conf");
+        Config config = Config.getInstance();
+        config.listTable();
+        String s;
+
+        // Latter overwrites former for root section
+        s = config.get("libdefaults", "default_realm");
+        if (s != null) {
+            throw new Exception();
+        }
+        // Latter overwrites former for strings
+        s = config.get("libdefaults", "default_tkt_enctypes");
+        if (!s.equals("aes256-cts")) {
+            throw new Exception();
+        }
+        // Latter overwrites former for sub-section
+        s = config.get("realms", "R1", "kdc");
+        if (!s.equals("k2")) {
+            throw new Exception(s);
+        }
+        // Duplicate keys in [realms] are merged
+        s = config.getAll("realms", "R2", "kdc");
+        if (!s.equals("k1 k2 k3 k4")) {
+            throw new Exception(s);
+        }
+        // Duplicate keys in [capaths] are merged
+        s = config.getAll("capaths", "R1", "R2");
+        if (!s.equals("R3 R4 R5 R6")) {
+            throw new Exception(s);
+        }
+        // We can be very deep now
+        s = config.get("new", "x", "y", "z", "a", "b", "c");
+        if (!s.equals("d")) {
+            throw new Exception(s);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/krb5/config/SCDynamicConfigTest.java	Mon Oct 29 14:14:06 2012 +0800
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2012, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7184246
+ * @summary Simplify Config.get() of krb5
+ */
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Hashtable;
+import java.util.Vector;
+import sun.security.krb5.Config;
+import sun.security.krb5.SCDynamicStoreConfig;
+
+public class SCDynamicConfigTest {
+
+    static Vector<Hashtable<String,String>>hosts() {
+        Vector <Hashtable<String,String>> result = new Vector<>();
+        Hashtable<String,String> pair = new Hashtable<>();
+        pair.put("host", "127.0.0.1");
+        result.add(pair);
+        pair = new Hashtable<>();
+        pair.put("host", "127.0.0.2");
+        result.add(pair);
+        return result;
+    }
+
+    public static void main(String[] args) throws Exception {
+        // Reconstruct a typical SCDynamicConfig.getKerberosConfig() output
+        Hashtable<String, Object> conf = new Hashtable<>();
+
+        Hashtable<String, Object> libdefaults = new Hashtable<>();
+        libdefaults.put("default_realm", "REALM.COM");
+        conf.put("libdefaults", libdefaults);
+
+        Hashtable<String, Object> realms = new Hashtable<>();
+        Hashtable<String, Object> thisRealm = new Hashtable<>();
+        realms.put("REALM.COM", thisRealm);
+        thisRealm.put("kpasswd", hosts());
+        thisRealm.put("kadmin", hosts());
+        thisRealm.put("kdc", hosts());
+        conf.put("realms", realms);
+
+        Hashtable<String, Object> domain_realm = new Hashtable<>();
+        domain_realm.put(".realm.com", "REALM.COM");
+        domain_realm.put("realm.com", "REALM.COM");
+        conf.put("domain_realm", domain_realm);
+
+        System.out.println("SCDynamicConfig:\n");
+        System.out.println(conf);
+
+        // Simulate SCDynamicConfig.getConfig() output
+        Method m = SCDynamicStoreConfig.class.getDeclaredMethod(
+                "convertNativeConfig", Hashtable.class);
+        m.setAccessible(true);
+        conf = (Hashtable)m.invoke(null, conf);
+
+        System.out.println("\nkrb5.conf:\n");
+        System.out.println(conf);
+
+        // Feed it into a Config object
+        System.setProperty("java.security.krb5.conf", "not-a-file");
+        Config cf = Config.getInstance();
+        Field f = Config.class.getDeclaredField("stanzaTable");
+        f.setAccessible(true);
+        f.set(cf, conf);
+
+        System.out.println("\nConfig:\n");
+        System.out.println(cf);
+
+        if (!cf.getDefaultRealm().equals("REALM.COM")) {
+            throw new Exception();
+        }
+        if (!cf.getKDCList("REALM.COM").equals("127.0.0.1 127.0.0.2")) {
+            throw new Exception();
+        }
+        if (!cf.get("domain_realm", ".realm.com").equals("REALM.COM")) {
+            throw new Exception();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/krb5/config/k1.conf	Mon Oct 29 14:14:06 2012 +0800
@@ -0,0 +1,40 @@
+[libdefaults]
+default_realm = R1
+
+[libdefaults]
+default_tkt_enctypes = aes128-cts
+default_tkt_enctypes = aes256-cts
+
+[realms]
+R1 = {
+    kdc = k1
+}
+R1 = {
+    kdc = k2
+}
+R2 = {
+    kdc = k1
+    kdc = k2 k3
+    admin_server = a1
+    kdc = k4
+}
+
+[capaths]
+R1 = {
+    R2 = R3
+    R2 = R4 R5
+    R2 = R6
+}
+
+[new]
+x = {
+    y = {
+        z = {
+            a = {
+                b = {
+                    c = d
+                }
+            }
+        }
+    }
+}