< prev index next >

test/sun/security/krb5/auto/ReplayCacheTestProc.java

Print this page
rev 15878 : 8168518: rcache interop with krb5-1.15

@@ -23,13 +23,14 @@
 
 /*
  * @test
  * @bug 7152176
  * @summary More krb5 tests
- * @library ../../../../java/security/testlibrary/
+ * @library ../../../../java/security/testlibrary/ /test/lib
  * @compile -XDignore.symbol.file ReplayCacheTestProc.java
- * @run main/othervm/timeout=100 ReplayCacheTestProc
+ * @run main/othervm/timeout=300 ReplayCacheTestProc
+ * @run main/othervm/timeout=300 -Djdk.krb5.rcache.usemd5=true ReplayCacheTestProc
  */
 
 import java.io.*;
 import java.nio.BufferUnderflowException;
 import java.nio.channels.SeekableByteChannel;

@@ -38,19 +39,39 @@
 import java.nio.file.StandardCopyOption;
 import java.nio.file.StandardOpenOption;
 import java.security.MessageDigest;
 import java.util.*;
 
+import jdk.test.lib.Platform;
 import sun.security.jgss.GSSUtil;
 import sun.security.krb5.internal.APReq;
 import sun.security.krb5.internal.rcache.AuthTime;
+import sun.security.krb5.internal.rcache.AuthTimeWithHash;
 
-// This test runs multiple acceptor Procs to mimin AP-REQ replays.
+/**
+ * This test runs multiple acceptor Procs to mimin AP-REQ replays.
+ * It can either run with automatic (random) test runs or user can provide
+ * these system properties:
+ *
+ * - test.libs on what types of acceptors to use
+ *   Format: CSV of (J|N|N<libname>)
+ *   Example: J,N,N/krb5-1.14/lib/libgssapi_krb5.so
+ *
+ * - test.runs on runs
+ *   Format: (req# | client# service#) acceptor# expected...
+ *   Example: c0h0J0v,c1h1N0v,r0J1x means 1st req is new c0 to h0 sent to J0,
+ *            2nd req is new c1 to h1 sent to N0,
+ *            3rd req is old (1st replayed) sent to J1.
+ *            For all old reqs, client# and service# MUST be -
+ *
+ * - test.autoruns on how many autoruns
+ *   Format: number
+ */
 public class ReplayCacheTestProc {
 
-    private static Proc[] ps;
-    private static Proc pc;
+    private static Proc[] pa;   // all acceptors
+    private static Proc pi;     // the single initiator
     private static List<Req> reqs = new ArrayList<>();
     private static String HOST = "localhost";
 
     // Where should the rcache be saved. It seems KRB5RCACHEDIR is not
     // recognized on Solaris. Maybe version too low? I see 1.6.

@@ -63,117 +84,171 @@
     private static long uid;
 
     public static void main0(String[] args) throws Exception {
         System.setProperty("java.security.krb5.conf", OneKDC.KRB5_CONF);
         if (args.length == 0) { // The controller
-            int ns = 5;     // number of servers
             int nu = 5;     // number of users
-            int nx = 50;    // number of experiments
-            int np = 5;     // number of peers (services)
-            int mode = 0;   // native(1), random(0), java(-1)
-            boolean random = true;      // random experiments choreograph
-
-            // Do not test interop with native GSS on some platforms
-            String os = System.getProperty("os.name", "???");
-            if (!os.startsWith("SunOS") && !os.startsWith("Linux")) {
-                mode = -1;
-            }
+            int nh = 5;     // number of hosts (services)
+            String[] libs;  // available acceptor types:
+                            // J: java
+                            // N: default native lib
+                            // N<libname>: native lib with the given name
+            Ex[] result;
+            int numPerType = 2; // number of servers per type
 
             uid = jdk.internal.misc.VM.geteuid();
 
             KDC kdc = KDC.create(OneKDC.REALM, HOST, 0, true);
             for (int i=0; i<nu; i++) {
                 kdc.addPrincipal(user(i), OneKDC.PASS);
             }
             kdc.addPrincipalRandKey("krbtgt/" + OneKDC.REALM);
-            for (int i=0; i<np; i++) {
-                kdc.addPrincipalRandKey(peer(i));
+            for (int i=0; i<nh; i++) {
+                kdc.addPrincipalRandKey(host(i));
             }
 
             kdc.writeKtab(OneKDC.KTAB);
             KDC.saveConfig(OneKDC.KRB5_CONF, kdc);
 
-            if (mode != -1) {
-                // A special native server to check basic sanity
-                if (ns(-1).waitFor() != 0) {
+            // User-provided libs
+            String userLibs = System.getProperty("test.libs");
+
+            if (userLibs != null) {
+                libs = userLibs.split(",");
+            } else {
+                if (Platform.isOSX() || Platform.isWindows()) {
+                    // macOS uses Heimdal and Windows has no native lib
+                    libs = new String[]{"J"};
+                } else {
+                    // Test interop between Java and native (if available)
+                    if (acceptor("N", "sanity").waitFor() != 0) {
                     Proc.d("Native mode sanity check failed, revert to java");
-                    mode = -1;
+                        libs = new String[]{"J"};
+                    } else {
+                        libs = new String[]{"J", "N"};
+                    }
                 }
             }
 
-            pc = Proc.create("ReplayCacheTestProc").debug("C")
-                    .args("client")
+            pi = Proc.create("ReplayCacheTestProc").debug("C")
+                    .args("initiator")
                     .start();
-            ps = new Proc[ns];
-            Ex[] result = new Ex[nx];
 
-            if (!random) {
-                // 2 experiments, 2 server, 1 peer, 1 user
-                nx = 2; ns = 2; np = 1; nu = 1;
-
-                // Creates reqs from user# to peer#
-                req(0, 0);
-
-                // Creates server#
-                ps[0] = ns(0);
-                ps[1] = js(1);
-
-                // Runs ex# using req# to server# with expected result
-                result[0] = round(0, 0, 0, true);
-                result[1] = round(1, 0, 1, false);
+            int na = libs.length * numPerType;  // total number of acceptors
+            pa = new Proc[na];
+
+            // Acceptors, numPerType for 1st, numForType for 2nd, ...
+            for (int i=0; i<na; i++) {
+                int type = i / numPerType;
+                String label; // N/J for N/J, ABC... for N<lib>
+                if (libs[type].length() == 1) {
+                    label = libs[type];
             } else {
-                Random r = new Random();
-                for (int i=0; i<ns; i++) {
-                    boolean useNative = (mode == 1) ? true
-                            : (mode == -1 ? false : r.nextBoolean());
-                    ps[i] = useNative?ns(i):js(i);
+                    label = "" + (char)('A' + type);
+                }
+                pa[i] = acceptor(libs[type], label + i % numPerType);
+            }
+
+            String userRuns = System.getProperty("test.runs");
+
+            if (userRuns != null) {
+                String[] runs = userRuns.split(",");
+                result = new Ex[runs.length];
+                for (int i = 0; i < runs.length; i++) {
+                    boolean expected = false;
+                    int req = -1;
+                    int client = -1;
+                    int host = -1;
+                    UserRun run = new UserRun(runs[i]);
+                    while (true) {
+                        char type = run.nextAction();
+                        if (type == ' ') {
+                            break;
+                        }
+                        switch (type) {
+                            case 'r':
+                                req = result[run.nextValue()].req;
+                                break;
+                            case 'c':
+                                client = run.nextValue();
+                                break;
+                            case 'h':
+                                req = req(client, run.nextValue());
+                                break;
+                            case 'J':
+                            case 'N':
+                                for (int j = 0; j < libs.length; j++) {
+                                    if (libs[j].equals("" + type)) {
+                                        host = j * numPerType + run.nextValue();
+                                        break;
+                                    }
+                                }
+                                break;
+                            case 'v':
+                                expected = true;
+                                break;
+                            case 'x':
+                                expected = false;
+                                break;
+                            default:    // ABC...
+                                host = (type - 'A') * numPerType + run.nextValue();
+                        }
                 }
-                for (int i=0; i<nx; i++) {
-                    result[i] = new Ex();
+                    result[i] = new Ex(i, req, host, expected);
+                }
+            } else {
+                result = new Ex[Integer.parseInt(
+                        System.getProperty("test.autoruns", "100"))];
+                Random r = new Random();
+                for (int i = 0; i < result.length; i++) {
                     int old;    // which req to send
                     boolean expected;
                     if (reqs.isEmpty() || r.nextBoolean()) {
-                        Proc.d("Console get new AP-REQ");
-                        old = req(r.nextInt(nu), r.nextInt(np));
+                        old = req(r.nextInt(nu), r.nextInt(nh));
                         expected = true;
                     } else {
-                        Proc.d("Console resue old");
                         old = r.nextInt(reqs.size());
                         expected = false;
                     }
-                    int s = r.nextInt(ns);
-                    Proc.d("Console send to " + s);
-                    result[i] = round(i, old, s, expected);
-                    Proc.d("Console sees " + result[i].actual);
+                    int s = r.nextInt(na);
+                    result[i] = new Ex(i, old, s, expected);
+                }
                 }
+
+            for (Ex x : result) {
+                x.run();
             }
 
-            pc.println("END");
-            for (int i=0; i<ns; i++) {
-                ps[i].println("END");
+            pi.println("END");
+            for (int i=0; i<na; i++) {
+                pa[i].println("END");
             }
             System.out.println("Result\n======");
             boolean finalOut = true;
-            for (int i=0; i<nx; i++) {
+            System.out.println("  #: expected (req): client host acceptor Result      size");
+            System.out.println("---- -------- ------ ------ ---- -------- ------      ----");
+            for (int i=0; i<result.length; i++) {
                 boolean out = result[i].expected==result[i].actual;
                 finalOut &= out;
-                System.out.printf("%3d: %s (%2d): u%d h%d %s %s   %s %2d\n",
+                System.out.printf("%3d: %8s (%3d):     u%d   h%d %8s   %s  %s %4d\n",
                         i,
                         result[i].expected?"----":"    ",
-                        result[i].old,
-                        result[i].user, result[i].peer, result[i].server,
+                        result[i].req,
+                        reqs.get(result[i].req).user,
+                        reqs.get(result[i].req).peer,
+                        pa[result[i].acceptor].debug(),
                         result[i].actual?"Good":"Bad ",
                         out?"   ":"xxx",
                         result[i].csize);
             }
             if (!finalOut) throw new Exception();
-        } else if (args[0].equals("N-1")) {
+        } else if (args[0].equals("sanity")) {
             // Native mode sanity check
             Proc.d("Detect start");
             Context s = Context.fromUserKtab("*", OneKDC.KTAB, true);
             s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
-        } else if (args[0].equals("client")) {
+        } else if (args[0].equals("initiator")) {
             while (true) {
                 String title = Proc.textIn();
                 Proc.d("Client see " + title);
                 if (title.equals("END")) break;
                 String[] cas = title.split(" ");

@@ -215,93 +290,81 @@
         }
     }
 
     // returns the user name
     private static String user(int p) {
-        return "USER" + p;
+        return "user" + p;
     }
-    // returns the peer name
-    private static String peer(int p) {
+
+    // returns the host name
+    private static String host(int p) {
         return "host" + p + "/" + HOST;
     }
+
     // returns the dfl name for a host
     private static String dfl(int p) {
-        return cwd + "host" + p + (uid == -1 ? "" : ("_"+uid));
+        return "host" + p + (uid == -1 ? "" : ("_"+uid));
     }
+
     // generates an ap-req and save into reqs, returns the index
     private static int req(int user, int peer) throws Exception {
-        pc.println(user(user) + " " + peer(peer));
-        Req req = new Req(user, peer, pc.readData());
+        pi.println(user(user) + " " + host(peer));
+        Req req = new Req(user, peer, pi.readData());
         reqs.add(req);
         return reqs.size() - 1;
     }
-    // carries out a round of experiment
-    // i: ex#, old: which req, server: which server, expected: result?
-    private static Ex round(int i, int old, int server, boolean expected)
-            throws Exception {
-        ps[server].println("TEST");
-        ps[server].println(reqs.get(old).msg);
-        String reply = ps[server].readData();
-        Ex result = new Ex();
-        result.i = i;
-        result.expected = expected;
-        result.server = ps[server].debug();
-        result.actual = Boolean.valueOf(reply);
-        result.user = reqs.get(old).user;
-        result.peer = reqs.get(old).peer;
-        result.old = old;
-        result.csize = csize(result.peer);
-        result.hash = hash(reqs.get(old).msg);
-        if (new File(dfl(result.peer)).exists()) {
-            Files.copy(Paths.get(dfl(result.peer)), Paths.get(
-                String.format("%03d-USER%d-host%d-%s-%s",
-                    i, result.user, result.peer, result.server,
-                    result.actual)
-                + "-" + result.hash),
-                StandardCopyOption.COPY_ATTRIBUTES);
-        }
-        return result;
+
+    // create a acceptor
+    private static Proc acceptor(String type, String label) throws Exception {
+        Proc p = Proc.create("ReplayCacheTestProc")
+                .args(label)
+                .debug(label);
+        if (type.equals("J")) {
+            p.prop("sun.security.krb5.rcache", "dfl")
+                    .prop("java.io.tmpdir", cwd);
+            String useMD5 = System.getProperty("jdk.krb5.rcache.usemd5");
+            if (useMD5 != null) {
+                p.prop("jdk.krb5.rcache.usemd5", useMD5);
     }
-    // create a native server
-    private static Proc ns(int i) throws Exception {
-        return Proc.create("ReplayCacheTestProc")
-                .args("N"+i)
-                .env("KRB5_CONFIG", OneKDC.KRB5_CONF)
+        } else {
+            p.env("KRB5_CONFIG", OneKDC.KRB5_CONF)
                 .env("KRB5_KTNAME", OneKDC.KTAB)
                 .env("KRB5RCACHEDIR", cwd)
                 .prop("sun.security.jgss.native", "true")
                 .prop("javax.security.auth.useSubjectCredsOnly", "false")
-                .prop("sun.security.nativegss.debug", "true")
-                .debug("N"+i)
-                .start();
+                    .prop("sun.security.nativegss.debug", "true");
+            if (type.length() > 1) {
+                String lib = type.substring(1);
+                String libDir = lib.substring(0, lib.lastIndexOf('/'));
+                p.prop("sun.security.jgss.lib", lib)
+                        .env("DYLD_LIBRARY_PATH", libDir)
+                        .env("LD_LIBRARY_PATH", libDir);
     }
-    // creates a java server
-    private static Proc js(int i) throws Exception {
-        return Proc.create("ReplayCacheTestProc")
-                .debug("S"+i)
-                .args("S"+i)
-                .prop("sun.security.krb5.rcache", "dfl")
-                .prop("java.io.tmpdir", cwd)
-                .start();
     }
+        return p.start();
+    }
+
     // generates hash of authenticator inside ap-req inside initsectoken
     private static String hash(String req) throws Exception {
         byte[] data = Base64.getDecoder().decode(req);
         data = Arrays.copyOfRange(data, 17, data.length);
-        byte[] hash = MessageDigest.getInstance("MD5").digest(new APReq(data).authenticator.getBytes());
+        byte[] hash = MessageDigest.getInstance(
+                AuthTimeWithHash.realAlg(AuthTimeWithHash.DEFAULT_HASH_ALG))
+                    .digest(new APReq(data).authenticator.getBytes());
         char[] h = new char[hash.length * 2];
         char[] hexConst = "0123456789ABCDEF".toCharArray();
         for (int i=0; i<hash.length; i++) {
             h[2*i] = hexConst[(hash[i]&0xff)>>4];
             h[2*i+1] = hexConst[hash[i]&0xf];
         }
         return new String(h);
     }
+
     // return size of dfl file, excluding the null hash ones
     private static int csize(int p) throws Exception {
         try (SeekableByteChannel chan = Files.newByteChannel(
-                Paths.get(dfl(p)), StandardOpenOption.READ)) {
+                Paths.get(cwd, dfl(p)), StandardOpenOption.READ)) {
             chan.position(6);
             int cc = 0;
             while (true) {
                 try {
                     if (AuthTime.readFrom(chan) != null) cc++;

@@ -312,29 +375,78 @@
             return cc;
         } catch (IOException ioe) {
             return 0;
         }
     }
+
     // models an experiement
     private static class Ex {
         int i;              // #
+        int req;            // which ap-req to send
+        int acceptor;       // which acceptor to send to
         boolean expected;   // expected result
+
         boolean actual;     // actual output
-        int old;            // which ap-req to send
-        String server;      // which server to send to
-        String hash;        // the hash of req
-        int user;           // which initiator
-        int peer;           // which acceptor
         int csize;          // size of rcache after test
+        String hash;        // the hash of req
+
+        Ex(int i, int req, int acceptor, boolean expected) {
+            this.i = i;
+            this.req = req;
+            this.acceptor = acceptor;
+            this.expected = expected;
+        }
+
+        void run() throws Exception {
+            Req r = reqs.get(req);
+            pa[acceptor].println("TEST");
+            pa[acceptor].println(r.msg);
+            String reply = pa[acceptor].readData();
+
+            actual = Boolean.valueOf(reply);
+            csize = csize(r.peer);
+            hash = hash(r.msg);
+            if (new File(cwd, dfl(r.peer)).exists()) {
+                Files.copy(Paths.get(cwd, dfl(r.peer)), Paths.get(
+                        String.format("%03d-USER%d-host%d-%s-%s",
+                                i, r.user, r.peer, acceptor,
+                                actual)
+                                + "-" + hash),
+                        StandardCopyOption.COPY_ATTRIBUTES);
+            }
+        }
     }
+
     // models a saved ap-req msg
     private static class Req {
         String msg;         // based64-ed req
-        int user;           // which initiator
-        int peer;           // which accceptor
+        int user;           // which client
+        int peer;           // which service
         Req(int user, int peer, String msg) {
             this.msg = msg;
             this.user= user;
             this.peer = peer;
         }
     }
+
+    private static class UserRun {
+        String run;
+        int pos = 0;
+        UserRun(String run) {
+            this.run = run;
+        }
+        char nextAction() {
+            return pos < run.length() ? run.charAt(pos++) : ' ';
+        }
+        int nextValue() {
+            int result = 0;
+            for (; pos < run.length(); pos++) {
+                char c = run.charAt(pos);
+                if (!Character.isDigit(c)) {
+                    break;
+                }
+                result = result * 10 + (c - '0');
+            }
+            return result;
+        }
+    }
 }
< prev index next >