jdk/src/windows/classes/sun/security/krb5/internal/tools/Ktab.java
changeset 7037 9fd3887c8010
parent 5506 202f599c92aa
child 7296 fa827ef8c75e
--- a/jdk/src/windows/classes/sun/security/krb5/internal/tools/Ktab.java	Wed Oct 27 22:10:37 2010 -0700
+++ b/jdk/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:");
     }
 }