changeset 3142:dfce5a0cc460

6950546: "ktab -d name etype" to "ktab -d name [-e etype] [kvno | all | old]" 6984764: kerberos fails if service side keytab is generated using JDK ktab Reviewed-by: valeriep
author weijun
date Thu, 28 Oct 2010 21:14:44 +0800
parents 4f91da528c68
children 7fee717f4707
files src/share/classes/sun/security/krb5/internal/ktab/KeyTab.java src/windows/classes/sun/security/krb5/internal/tools/Ktab.java test/sun/security/krb5/auto/KDC.java test/sun/security/krb5/tools/KtabCheck.java test/sun/security/krb5/tools/ktcheck.sh test/sun/security/krb5/tools/onlythree.conf
diffstat 6 files changed, 443 insertions(+), 234 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/sun/security/krb5/internal/ktab/KeyTab.java	Wed Oct 27 22:10:37 2010 -0700
+++ b/src/share/classes/sun/security/krb5/internal/ktab/KeyTab.java	Thu Oct 28 21:14:44 2010 +0800
@@ -34,7 +34,6 @@
 import sun.security.krb5.*;
 import sun.security.krb5.internal.*;
 import sun.security.krb5.internal.crypto.*;
-import java.util.Vector;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.io.IOException;
@@ -42,7 +41,10 @@
 import java.io.FileOutputStream;
 import java.io.File;
 import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.StringTokenizer;
+import java.util.Vector;
 
 /**
  * This class represents key table. The key table functions deal with storing
@@ -239,23 +241,22 @@
         EncryptionKey key;
         int size = entries.size();
         ArrayList<EncryptionKey> keys = new ArrayList<EncryptionKey> (size);
-        if (entries != null) {
-            for (int i = size-1; i >= 0; i--) {
-                entry = entries.elementAt(i);
-                if (entry.service.match(service)) {
-                    if (EType.isSupported(entry.keyType)) {
-                        key = new EncryptionKey(entry.keyblock,
-                                            entry.keyType,
-                                            new Integer(entry.keyVersion));
-                        keys.add(key);
-                        if (DEBUG) {
-                            System.out.println("Added key: " + entry.keyType +
-                                "version: " + entry.keyVersion);
-                        }
-                    } else if (DEBUG) {
-                        System.out.println("Found unsupported keytype (" +
-                            entry.keyType + ") for " + service);
+
+        for (int i = size-1; i >= 0; i--) {
+            entry = entries.elementAt(i);
+            if (entry.service.match(service)) {
+                if (EType.isSupported(entry.keyType)) {
+                    key = new EncryptionKey(entry.keyblock,
+                                        entry.keyType,
+                                        new Integer(entry.keyVersion));
+                    keys.add(key);
+                    if (DEBUG) {
+                        System.out.println("Added key: " + entry.keyType +
+                            "version: " + entry.keyVersion);
                     }
+                } else if (DEBUG) {
+                    System.out.println("Found unsupported keytype (" +
+                        entry.keyType + ") for " + service);
                 }
             }
         }
@@ -313,16 +314,14 @@
      */
     public boolean findServiceEntry(PrincipalName service) {
         KeyTabEntry entry;
-        if (entries != null) {
-            for (int i = 0; i < entries.size(); i++) {
-                entry = entries.elementAt(i);
-                if (entry.service.match(service)) {
-                    if (EType.isSupported(entry.keyType)) {
-                        return true;
-                    } else if (DEBUG) {
-                        System.out.println("Found unsupported keytype (" +
-                            entry.keyType + ") for " + service);
-                    }
+        for (int i = 0; i < entries.size(); i++) {
+            entry = entries.elementAt(i);
+            if (entry.service.match(service)) {
+                if (EType.isSupported(entry.keyType)) {
+                    return true;
+                } else if (DEBUG) {
+                    System.out.println("Found unsupported keytype (" +
+                        entry.keyType + ") for " + service);
                 }
             }
         }
@@ -337,94 +336,57 @@
      * Adds a new entry in the key table.
      * @param service the service which will have a new entry in the key table.
      * @param psswd the password which generates the key.
+     * @param kvno the kvno to use, -1 means automatic increasing
+     * @param append false if entries with old kvno would be removed.
+     * Note: if kvno is not -1, entries with the same kvno are always removed
      */
-    public void addEntry(PrincipalName service, char[] psswd)
-         throws KrbException {
+    public void addEntry(PrincipalName service, char[] psswd,
+            int kvno, boolean append) throws KrbException {
 
         EncryptionKey[] encKeys = EncryptionKey.acquireSecretKeys(
             psswd, service.getSalt());
 
+        // There should be only one maximum KVNO value for all etypes, so that
+        // all added keys can have the same KVNO.
+
+        int maxKvno = 0;    // only useful when kvno == -1
+        for (int i = entries.size()-1; i >= 0; i--) {
+            KeyTabEntry e = entries.get(i);
+            if (e.service.match(service)) {
+                if (e.keyVersion > maxKvno) {
+                    maxKvno = e.keyVersion;
+                }
+                if (!append || e.keyVersion == kvno) {
+                    entries.removeElementAt(i);
+                }
+            }
+        }
+        if (kvno == -1) {
+            kvno = maxKvno + 1;
+        }
+
         for (int i = 0; encKeys != null && i < encKeys.length; i++) {
             int keyType = encKeys[i].getEType();
             byte[] keyValue = encKeys[i].getBytes();
-            int result = retrieveEntry(service, keyType);
-            int kvno = 1;
-            if (result != -1) {
-                KeyTabEntry oldEntry = entries.elementAt(result);
-                kvno = oldEntry.keyVersion;
-                entries.removeElementAt(result);
-                kvno += 1;
-            } else
-                kvno = 1;
 
             KeyTabEntry newEntry = new KeyTabEntry(service,
                             service.getRealm(),
                             new KerberosTime(System.currentTimeMillis()),
                                                kvno, keyType, keyValue);
-            if (entries == null)
-                entries = new Vector<KeyTabEntry> ();
             entries.addElement(newEntry);
         }
     }
 
     /**
-     * Only used by KDC test. This method can specify kvno and does not
-     * remove any old keys.
-     */
-    public void addEntry(PrincipalName service, char[] psswd, int kvno)
-         throws KrbException {
-
-        EncryptionKey[] encKeys = EncryptionKey.acquireSecretKeys(
-            psswd, service.getSalt());
-
-        for (int i = 0; encKeys != null && i < encKeys.length; i++) {
-            int keyType = encKeys[i].getEType();
-            byte[] keyValue = encKeys[i].getBytes();
-            KeyTabEntry newEntry = new KeyTabEntry(service,
-                            service.getRealm(),
-                            new KerberosTime(System.currentTimeMillis()),
-                                               kvno, keyType, keyValue);
-            if (entries == null)
-                entries = new Vector<KeyTabEntry> ();
-            entries.addElement(newEntry);
-        }
-    }
-
-    /**
-     * Retrieves the key table entry with the specified service name.
-     * @param service the service which may have an entry in the key table.
-     * @param keyType the etype to match, returns the 1st one if -1 provided
-     * @return -1 if the entry is not found, else return the entry index
-     * in the list.
-     */
-    private int retrieveEntry(PrincipalName service, int keyType) {
-        KeyTabEntry e;
-        if (entries != null) {
-            for (int i = 0; i < entries.size(); i++) {
-                e = entries.elementAt(i);
-                if (service.match(e.getService()) &&
-                    (keyType == -1 || e.keyType == keyType)) {
-                    return i;
-                }
-            }
-        }
-        return -1;
-    }
-
-    /**
      * Gets the list of service entries in key table.
      * @return array of <code>KeyTabEntry</code>.
      */
     public KeyTabEntry[] getEntries() {
-        if (entries != null) {
-            KeyTabEntry[] kentries = new KeyTabEntry[entries.size()];
-            for (int i = 0; i < kentries.length; i++) {
-                kentries[i] = entries.elementAt(i);
-            }
-            return kentries;
-        } else {
-            return null;
+        KeyTabEntry[] kentries = new KeyTabEntry[entries.size()];
+        for (int i = 0; i < kentries.length; i++) {
+            kentries[i] = entries.elementAt(i);
         }
+        return kentries;
     }
 
     /**
@@ -464,29 +426,55 @@
     }
 
     /**
-     * Removes an entry from the key table.
+     * Removes entries from the key table.
      * @param service the service <code>PrincipalName</code>.
-     * @param etype the etype to match, first one if -1 provided
-     * @return 1 if removed successfully, 0 otherwise
+     * @param etype the etype to match, remove all if -1
+     * @param kvno what kvno to remove, -1 for all, -2 for old
+     * @return the number of entries deleted
      */
-    public int deleteEntry(PrincipalName service, int etype) {
-        int result = retrieveEntry(service, etype);
-        if (result != -1) {
-            entries.removeElementAt(result);
-            return 1;
+    public int deleteEntries(PrincipalName service, int etype, int kvno) {
+        int count = 0;
+
+        // Remember the highest KVNO for each etype. Used for kvno == -2
+        Map<Integer,Integer> highest = new HashMap<>();
+
+        for (int i = entries.size()-1; i >= 0; i--) {
+            KeyTabEntry e = entries.get(i);
+            if (service.match(e.getService())) {
+                if (etype == -1 || e.keyType == etype) {
+                    if (kvno == -2) {
+                        // Two rounds for kvno == -2. In the first round (here),
+                        // only find out highest KVNO for each etype
+                        if (highest.containsKey(e.keyType)) {
+                            int n = highest.get(e.keyType);
+                            if (e.keyVersion > n) {
+                                highest.put(e.keyType, e.keyVersion);
+                            }
+                        } else {
+                            highest.put(e.keyType, e.keyVersion);
+                        }
+                    } else if (kvno == -1 || e.keyVersion == kvno) {
+                        entries.removeElementAt(i);
+                        count++;
+                    }
+                }
+            }
         }
-        return 0;
-    }
 
-    /**
-     * Removes an entry from the key table.
-     * @param service the service <code>PrincipalName</code>.
-     * @return number of entries removed
-     */
-    public int deleteEntry(PrincipalName service) {
-        int count = 0;
-        while (deleteEntry(service, -1) > 0) {
-            count++;
+        // Second round for kvno == -2, remove old entries
+        if (kvno == -2) {
+            for (int i = entries.size()-1; i >= 0; i--) {
+                KeyTabEntry e = entries.get(i);
+                if (service.match(e.getService())) {
+                    if (etype == -1 || e.keyType == etype) {
+                        int n = highest.get(e.keyType);
+                        if (e.keyVersion != n) {
+                            entries.removeElementAt(i);
+                            count++;
+                        }
+                    }
+                }
+            }
         }
         return count;
     }
--- a/src/windows/classes/sun/security/krb5/internal/tools/Ktab.java	Wed Oct 27 22:10:37 2010 -0700
+++ b/src/windows/classes/sun/security/krb5/internal/tools/Ktab.java	Thu Oct 28 21:14:44 2010 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2010, 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
@@ -39,10 +39,11 @@
 import java.text.DateFormat;
 import java.util.Arrays;
 import java.util.Date;
+import java.util.Locale;
 import sun.security.krb5.internal.crypto.EType;
 /**
  * This class can execute as a command-line tool to help the user manage
- * entires in the key table.
+ * entries in the key table.
  * Available functions include list/add/update/delete service key(s).
  *
  * @author Yanni Zhang
@@ -60,30 +61,20 @@
     int etype = -1;
     char[] password = null;
 
+    boolean forced = false; // true if delete without prompt. Default false
+    boolean append = false; // true if new keys are appended. Default false
+    int vDel = -1;          // kvno to delete, -1 all, -2 old. Default -1
+    int vAdd = -1;          // kvno to add. Default -1, means auto incremented
+
     /**
      * The main program that can be invoked at command line.
-     * <br>Usage: ktab <options>
-     * <br>available options to Ktab:
-     * <ul>
-     * <li><b>-l [-e] [-t]</b>  list the keytab name and entries, -e show
-     * encryption etypes, -t show timestamps.
-     * <li><b>-a</b>  &lt;<i>principal name</i>&gt;
-     * (&lt;<i>password</i>&gt;)  add an entry to the keytab.
-     * The entry is added only to the keytab. No changes are made to the
-     * Kerberos database.
-     * <li><b>-d</b>  &lt;<i>principal name</i>&gt; [&lt;<i>etype</i>&gt;]
-     * delete an entry from the keytab.
-     * The entry is deleted only from the keytab. No changes are made to the
-     * Kerberos database.
-     * <li><b>-k</b>  &lt;<i>keytab name</i> &gt;
-     * specify keytab name and path with prefix FILE:
-     * <li><b>-help</b> display instructions.
+     * See {@link #printHelp} for usages.
      */
     public static void main(String[] args) {
         Ktab ktab = new Ktab();
         if ((args.length == 1) && (args[0].equalsIgnoreCase("-help"))) {
             ktab.printHelp();
-            System.exit(0);
+            return;
         } else if ((args == null) || (args.length == 0)) {
             ktab.action = 'l';
         } else {
@@ -139,7 +130,6 @@
             break;
         default:
             ktab.printHelp();
-            System.exit(-1);
         }
     }
 
@@ -147,84 +137,129 @@
      * Parses the command line arguments.
      */
     void processArgs(String[] args) {
-        Character arg = null;
+
+        // Commands (should appear before options):
+        //   -l
+        //   -a <princ>
+        //   -d <princ>
+        // Options:
+        //   -e <etype> (for -d)
+        //   -e (for -l)
+        //   -n <kvno>
+        //   -k <keytab>
+        //   -t
+        //   -f
+        //   -append
+        // Optional extra arguments:
+        //   password for -a
+        //   [kvno|all|old] for -d
+
+        boolean argAlreadyAppeared = false;
         for (int i = 0; i < args.length; i++) {
-            if ((args[i].length() == 2) && (args[i].startsWith("-"))) {
-                arg = new Character(args[i].charAt(1));
-            } else {
-                printHelp();
-                System.exit(-1);
-            }
-            switch (arg.charValue()) {
-            case 'l':
-            case 'L':
-                action = 'l';    // list keytab location, name and entries
-                break;
-            case 'a':
-            case 'A':
-                action = 'a'; // add a new entry to keytab.
-                i++;
-                if ((i < args.length) && (!args[i].startsWith("-"))) {
-                    principal = args[i];
-                } else {
-                    System.out.println("Please specify the principal name"+
-                                       " after -a option.");
-                    printHelp();
-                    System.exit(-1);
+            if (args[i].startsWith("-")) {
+                switch (args[i].toLowerCase(Locale.US)) {
+
+                    // Commands
+                    case "-l":   // list
+                        action = 'l';
+                        break;
+                    case "-a":   // add a new entry to keytab.
+                        action = 'a';
+                        if (++i >= args.length || args[i].startsWith("-")) {
+                            error("A principal name must be specified after -a");
+                        }
+                        principal = args[i];
+                        break;
+                    case "-d":   // delete entries
+                        action = 'd';
+                        if (++i >= args.length || args[i].startsWith("-")) {
+                            error("A principal name must be specified after -d");
+                        }
+                        principal = args[i];
+                        break;
+
+                        // Options
+                    case "-e":
+                        if (action == 'l') {    // list etypes
+                            showEType = true;
+                        } else if (action == 'd') { // delete etypes
+                            if (++i >= args.length || args[i].startsWith("-")) {
+                                error("An etype must be specified after -e");
+                            }
+                            try {
+                                etype = Integer.parseInt(args[i]);
+                                if (etype <= 0) {
+                                    throw new NumberFormatException();
+                                }
+                            } catch (NumberFormatException nfe) {
+                                error(args[i] + " is not a valid etype");
+                            }
+                        } else {
+                            error(args[i] + " is not valid after -" + action);
+                        }
+                        break;
+                    case "-n":   // kvno for -a
+                        if (++i >= args.length || args[i].startsWith("-")) {
+                            error("A KVNO must be specified after -n");
+                        }
+                        try {
+                            vAdd = Integer.parseInt(args[i]);
+                            if (vAdd < 0) {
+                                throw new NumberFormatException();
+                            }
+                        } catch (NumberFormatException nfe) {
+                            error(args[i] + " is not a valid KVNO");
+                        }
+                        break;
+                    case "-k":  // specify keytab to use
+                        if (++i >= args.length || args[i].startsWith("-")) {
+                            error("A keytab name must be specified after -k");
+                        }
+                        if (args[i].length() >= 5 &&
+                            args[i].substring(0, 5).equalsIgnoreCase("FILE:")) {
+                            name = args[i].substring(5);
+                        } else {
+                            name = args[i];
+                        }
+                        break;
+                    case "-t":   // list timestamps
+                        showTime = true;
+                        break;
+                    case "-f":   // force delete, no prompt
+                        forced = true;
+                        break;
+                    case "-append": // -a, new keys append to file
+                        append = true;
+                        break;
+                    default:
+                        printHelp();
+                        break;
                 }
-                if ((i + 1 < args.length) &&
-                    (!args[i + 1].startsWith("-"))) {
-                    password = args[i + 1].toCharArray();
-                    i++;
-                } else {
-                    password = null; // prompt user for password later.
+            } else {    // optional standalone arguments
+                if (argAlreadyAppeared) {
+                    error("Useless extra argument " + args[i]);
                 }
-                break;
-            case 'd':
-            case 'D':
-                action = 'd'; // delete an entry.
-                i++;
-                if ((i < args.length) && (!args[i].startsWith("-"))) {
-                    principal = args[i];
-                    int j = i + 1;
-                    if ((j < args.length) && (!args[j].startsWith("-"))) {
-                        etype = Integer.parseInt(args[j]);
-                        i = j;
+                if (action == 'a') {
+                    password = args[i].toCharArray();
+                } else if (action == 'd') {
+                    switch (args[i]) {
+                        case "all": vDel = -1; break;
+                        case "old": vDel = -2; break;
+                        default: {
+                            try {
+                                vDel = Integer.parseInt(args[i]);
+                                if (vDel < 0) {
+                                    throw new NumberFormatException();
+                                }
+                            } catch (NumberFormatException nfe) {
+                                error(args[i] + " is not a valid KVNO");
+                            }
+                        }
                     }
                 } else {
-                    System.out.println("Please specify the principal" +
-                                       "name of the entry you want to " +
-                                       " delete after -d option.");
-                    printHelp();
-                    System.exit(-1);
+                    error("Useless extra argument " + args[i]);
                 }
-                break;
-            case 'k':
-            case 'K':
-                i++;
-                if ((i < args.length) && (!args[i].startsWith("-"))) {
-                    if (args[i].length() >= 5 &&
-                        args[i].substring(0, 5).equalsIgnoreCase("FILE:")) {
-                        name = args[i].substring(5);
-                    } else
-                        name = args[i];
-                } else {
-                    System.out.println("Please specify the keytab "+
-                                       "file name and location " +
-                                       "after -k option");
-                    printHelp();
-                    System.exit(-1);
-                }
-                break;
-            case 'e':
-                showEType = true;
-                break;
-            case 't':
-                showTime = true;
-                break;
-            default:
-                printHelp();
-                System.exit(-1);
+                argAlreadyAppeared = true;
             }
         }
     }
@@ -263,7 +298,7 @@
         }
         try {
             // admin.addEntry(pname, password);
-            table.addEntry(pname, password);
+            table.addEntry(pname, password, vAdd, append);
             Arrays.fill(password, '0');  // clear password
             // admin.save();
             table.save();
@@ -350,23 +385,25 @@
             if (pname.getRealm() == null) {
                 pname.setRealm(Config.getInstance().getDefaultRealm());
             }
-            String answer;
-            BufferedReader cis =
-                new BufferedReader(new InputStreamReader(System.in));
-            System.out.print("Are you sure you want to"+
-                             " delete service key for " + pname.toString() +
-                             " (" + (etype==-1?"all etypes":("etype = "+etype)) +
-                             ") in " + table.tabName() + "?(Y/N): ");
+            if (!forced) {
+                String answer;
+                BufferedReader cis =
+                    new BufferedReader(new InputStreamReader(System.in));
+                System.out.print("Are you sure you want to delete "+
+                        "service key(s) for " + pname.toString() +
+                        " (" + (etype==-1?"all etypes":("etype="+etype)) + ", " +
+                        (vDel==-1?"all kvno":(vDel==-2?"old kvno":("kvno=" + vDel))) +
+                        ") in " + table.tabName() + "? (Y/[N]): ");
 
-            System.out.flush();
-            answer = cis.readLine();
-            if (answer.equalsIgnoreCase("Y") ||
-                answer.equalsIgnoreCase("Yes"));
-            else {
-                // no error, the user did not want to delete the entry
-                System.exit(0);
+                System.out.flush();
+                answer = cis.readLine();
+                if (answer.equalsIgnoreCase("Y") ||
+                    answer.equalsIgnoreCase("Yes"));
+                else {
+                    // no error, the user did not want to delete the entry
+                    System.exit(0);
+                }
             }
-
         } catch (KrbException e) {
             System.err.println("Error occured while deleting the entry. "+
                                "Deletion failed.");
@@ -379,9 +416,7 @@
             System.exit(-1);
         }
 
-        int count;
-        if (etype == -1) count = table.deleteEntry(pname);
-        else count = table.deleteEntry(pname, etype);
+        int count = table.deleteEntries(pname, etype, vDel);
 
         if (count == 0) {
             System.err.println("No matched entry in the keytab. " +
@@ -396,23 +431,47 @@
                 e.printStackTrace();
                 System.exit(-1);
             }
-            System.out.println("Done!");
+            System.out.println("Done! " + count + " entries removed.");
         }
     }
 
+    void error(String... errors) {
+        for (String error: errors) {
+            System.out.println("Error: " + error + ".");
+        }
+        printHelp();
+        System.exit(-1);
+    }
     /**
      * Prints out the help information.
      */
     void printHelp() {
-        System.out.println("\nUsage: ktab " +
-                           "<options>");
-        System.out.println("available options to Ktab:");
-        System.out.println("-l [-e] [-t]\t\t\tlist the keytab name and entries,\n\t\t\t\t-e with etype, -t with timestamp");
-        System.out.println("-a <principal name> (<password>)add an entry " +
-                           "to the keytab");
-        System.out.println("-d <principal name> [<etype>]\tdelete an "+
-                           "entry from the keytab");
-        System.out.println("-k <keytab name>\t\tspecify keytab name and "+
-                           "path with prefix FILE:");
+        System.out.println("\nUsage: ktab <commands> <options>");
+        System.out.println();
+        System.out.println("Available commands:");
+        System.out.println();
+        System.out.println("-l [-e] [-t]\n"
+                + "    list the keytab name and entries. -e with etype, -t with timestamp.");
+        System.out.println("-a <principal name> [<password>] [-n <kvno>] [-append]\n"
+                + "    add new key entries to the keytab for the given principal name with\n"
+                + "    optional <password>. If a <kvno> is specified, new keys' Key Version\n"
+                + "    Numbers equal to the value, otherwise, automatically incrementing\n"
+                + "    the Key Version Numbers. If -append is specified, new keys are\n"
+                + "    appended to the keytab, otherwise, old keys for the\n"
+                + "    same principal are removed.");
+        System.out.println("-d <principal name> [-f] [-e <etype>] [<kvno> | all | old]\n"
+                + "    delete key entries from the keytab for the specified principal. If\n"
+                + "    <kvno> is specified, delete keys whose Key Version Numbers match\n"
+                + "    kvno. If \"all\" is specified, delete all keys. If \"old\" is specified,\n"
+                + "    delete all keys except those with the highest kvno. Default action\n"
+                + "    is \"all\". If <etype> is specified, only keys of this encryption type\n"
+                + "    are deleted. <etype> should be specified as the numberic value etype\n"
+                + "    defined in RFC 3961, section 8. A prompt to confirm the deletion is\n"
+                + "    displayed unless -f is specified.");
+        System.out.println();
+        System.out.println("Common option(s):");
+        System.out.println();
+        System.out.println("-k <keytab name>\n"
+                + "    specify keytab name and path with prefix FILE:");
     }
 }
--- a/test/sun/security/krb5/auto/KDC.java	Wed Oct 27 22:10:37 2010 -0700
+++ b/test/sun/security/krb5/auto/KDC.java	Thu Oct 28 21:14:44 2010 +0800
@@ -245,7 +245,7 @@
                         name.indexOf('/') < 0 ?
                             PrincipalName.KRB_NT_UNKNOWN :
                             PrincipalName.KRB_NT_SRV_HST),
-                            kdc.passwords.get(name));
+                            kdc.passwords.get(name), -1, true);
             }
         }
         ktab.save();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/krb5/tools/KtabCheck.java	Thu Oct 28 21:14:44 2010 +0800
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import sun.security.krb5.internal.ktab.KeyTab;
+import sun.security.krb5.internal.ktab.KeyTabEntry;
+
+/**
+ * This class is called by the test ktcheck.sh and is not meant to run
+ * by itself.
+ */
+public class KtabCheck {
+    /**
+     * Checks if a keytab contains exactly the keys (kvno and etype)
+     * @param args keytabname kvno etype...
+     */
+    public static void main(String[] args) throws Exception {
+        System.out.println("Checking " + Arrays.toString(args));
+        KeyTab ktab = KeyTab.getInstance(args[0]);
+        Set<String> expected = new HashSet<String>();
+        for (int i=1; i<args.length; i += 2) {
+            expected.add(args[i]+":"+args[i+1]);
+        }
+        for (KeyTabEntry e: ktab.getEntries()) {
+            // KVNO and etype
+            String vne = e.getKey().getKeyVersionNumber() + ":" +
+                    e.getKey().getEType();
+            if (!expected.contains(vne)) {
+                throw new Exception("No " + vne + " in expected");
+            }
+            expected.remove(vne);
+        }
+        if (!expected.isEmpty()) {
+            throw new Exception("Extra elements in expected");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/krb5/tools/ktcheck.sh	Thu Oct 28 21:14:44 2010 +0800
@@ -0,0 +1,94 @@
+#
+# Copyright (c) 2010, 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 6950546
+# @summary "ktab -d name etype" to "ktab -d name [-e etype] [kvno | all | old]"
+# @run shell ktcheck.sh
+#
+
+if [ "${TESTJAVA}" = "" ] ; then
+  JAVAC_CMD=`which javac`
+  TESTJAVA=`dirname $JAVAC_CMD`/..
+fi
+
+if [ "${TESTSRC}" = "" ] ; then
+  TESTSRC="."
+fi
+
+OS=`uname -s`
+case "$OS" in
+  CYGWIN* )
+    FS="/"
+    ;;
+  Windows_* )
+    FS="\\"
+    ;;
+  * )
+    FS="/"
+    echo "Unsupported system!"
+    exit 0;
+    ;;
+esac
+
+KEYTAB=ktab.tmp
+
+rm $KEYTAB
+${TESTJAVA}${FS}bin${FS}javac -d . ${TESTSRC}${FS}KtabCheck.java
+
+EXTRA_OPTIONS="-Djava.security.krb5.conf=${TESTSRC}${FS}onlythree.conf"
+KTAB="${TESTJAVA}${FS}bin${FS}ktab -J${EXTRA_OPTIONS} -k $KEYTAB -f"
+CHECK="${TESTJAVA}${FS}bin${FS}java ${EXTRA_OPTIONS} KtabCheck $KEYTAB"
+
+echo ${EXTRA_OPTIONS}
+
+$KTAB -a me mine
+$CHECK 1 16 1 23 1 17 || exit 1
+$KTAB -a me mine -n 0
+$CHECK 0 16 0 23 0 17 || exit 1
+$KTAB -a me mine -n 1 -append
+$CHECK 0 16 0 23 0 17 1 16 1 23 1 17 || exit 1
+$KTAB -a me mine -append
+$CHECK 0 16 0 23 0 17 1 16 1 23 1 17 2 16 2 23 2 17 || exit 1
+$KTAB -a me mine
+$CHECK 3 16 3 23 3 17 || exit 1
+$KTAB -a me mine -n 4 -append
+$CHECK 3 16 3 23 3 17 4 16 4 23 4 17 || exit 1
+$KTAB -a me mine -n 5 -append
+$CHECK 3 16 3 23 3 17 4 16 4 23 4 17 5 16 5 23 5 17 || exit 1
+$KTAB -a me mine -n 6 -append
+$CHECK 3 16 3 23 3 17 4 16 4 23 4 17 5 16 5 23 5 17 6 16 6 23 6 17 || exit 1
+$KTAB -d me 3
+$CHECK 4 16 4 23 4 17 5 16 5 23 5 17 6 16 6 23 6 17 || exit 1
+$KTAB -d me -e 16 6
+$CHECK 4 16 4 23 4 17 5 16 5 23 5 17 6 23 6 17 || exit 1
+$KTAB -d me -e 17 6
+$CHECK 4 16 4 23 4 17 5 16 5 23 5 17 6 23 || exit 1
+$KTAB -d me -e 16 5
+$CHECK 4 16 4 23 4 17 5 23 5 17 6 23 || exit 1
+$KTAB -d me old
+$CHECK 4 16 5 17 6 23 || exit 1
+$KTAB -d me old
+$CHECK 4 16 5 17 6 23 || exit 1
+$KTAB -d me
+$CHECK || exit 1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/krb5/tools/onlythree.conf	Thu Oct 28 21:14:44 2010 +0800
@@ -0,0 +1,9 @@
+[libdefaults]
+default_realm = LOCAL.COM
+default_tkt_enctypes = des3-cbc-sha1 rc4-hmac aes128-cts
+
+[realms]
+LOCAL.COM = {
+    kdc = localhost
+}
+