< 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 >