< prev index next >
test/sun/security/krb5/auto/ReplayCacheTestProc.java
Print this page
rev 15878 : 8168518: rcache interop with krb5-1.15
*** 23,35 ****
/*
* @test
* @bug 7152176
* @summary More krb5 tests
! * @library ../../../../java/security/testlibrary/
* @compile -XDignore.symbol.file ReplayCacheTestProc.java
! * @run main/othervm/timeout=100 ReplayCacheTestProc
*/
import java.io.*;
import java.nio.BufferUnderflowException;
import java.nio.channels.SeekableByteChannel;
--- 23,36 ----
/*
* @test
* @bug 7152176
* @summary More krb5 tests
! * @library ../../../../java/security/testlibrary/ /test/lib
* @compile -XDignore.symbol.file ReplayCacheTestProc.java
! * @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,56 ****
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.util.*;
import sun.security.jgss.GSSUtil;
import sun.security.krb5.internal.APReq;
import sun.security.krb5.internal.rcache.AuthTime;
! // This test runs multiple acceptor Procs to mimin AP-REQ replays.
public class ReplayCacheTestProc {
! private static Proc[] ps;
! private static Proc pc;
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.
--- 39,77 ----
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.
! * 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[] 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,179 ****
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;
! }
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));
}
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) {
Proc.d("Native mode sanity check failed, revert to java");
! mode = -1;
}
}
! pc = Proc.create("ReplayCacheTestProc").debug("C")
! .args("client")
.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);
} 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);
}
! for (int i=0; i<nx; i++) {
! result[i] = new Ex();
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));
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);
}
}
! pc.println("END");
! for (int i=0; i<ns; i++) {
! ps[i].println("END");
}
System.out.println("Result\n======");
boolean finalOut = true;
! for (int i=0; i<nx; 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",
i,
result[i].expected?"----":" ",
! result[i].old,
! result[i].user, result[i].peer, result[i].server,
result[i].actual?"Good":"Bad ",
out?" ":"xxx",
result[i].csize);
}
if (!finalOut) throw new Exception();
! } else if (args[0].equals("N-1")) {
// 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")) {
while (true) {
String title = Proc.textIn();
Proc.d("Client see " + title);
if (title.equals("END")) break;
String[] cas = title.split(" ");
--- 84,254 ----
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 nu = 5; // number of users
! 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<nh; i++) {
! kdc.addPrincipalRandKey(host(i));
}
kdc.writeKtab(OneKDC.KTAB);
KDC.saveConfig(OneKDC.KRB5_CONF, kdc);
! // 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");
! libs = new String[]{"J"};
! } else {
! libs = new String[]{"J", "N"};
! }
}
}
! pi = Proc.create("ReplayCacheTestProc").debug("C")
! .args("initiator")
.start();
! 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 {
! 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();
! }
}
! 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()) {
! old = req(r.nextInt(nu), r.nextInt(nh));
expected = true;
} else {
old = r.nextInt(reqs.size());
expected = false;
}
! int s = r.nextInt(na);
! result[i] = new Ex(i, old, s, expected);
! }
}
+
+ for (Ex x : result) {
+ x.run();
}
! pi.println("END");
! for (int i=0; i<na; i++) {
! pa[i].println("END");
}
System.out.println("Result\n======");
boolean finalOut = true;
! 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: %8s (%3d): u%d h%d %8s %s %s %4d\n",
i,
result[i].expected?"----":" ",
! 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("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("initiator")) {
while (true) {
String title = Proc.textIn();
Proc.d("Client see " + title);
if (title.equals("END")) break;
String[] cas = title.split(" ");
*** 215,307 ****
}
}
// returns the user name
private static String user(int p) {
! return "USER" + p;
}
! // returns the peer name
! private static String peer(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));
}
// 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());
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 native server
! private static Proc ns(int i) throws Exception {
! return Proc.create("ReplayCacheTestProc")
! .args("N"+i)
! .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();
}
- // 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();
}
// 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());
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)) {
chan.position(6);
int cc = 0;
while (true) {
try {
if (AuthTime.readFrom(chan) != null) cc++;
--- 290,370 ----
}
}
// returns the user name
private static String user(int p) {
! return "user" + 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 "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 {
! pi.println(user(user) + " " + host(peer));
! Req req = new Req(user, peer, pi.readData());
reqs.add(req);
return reqs.size() - 1;
}
!
! // 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);
}
! } 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");
! 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);
}
}
+ 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(
! 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(cwd, dfl(p)), StandardOpenOption.READ)) {
chan.position(6);
int cc = 0;
while (true) {
try {
if (AuthTime.readFrom(chan) != null) cc++;
*** 312,340 ****
return cc;
} catch (IOException ioe) {
return 0;
}
}
// models an experiement
private static class Ex {
int i; // #
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
}
// models a saved ap-req msg
private static class Req {
String msg; // based64-ed req
! int user; // which initiator
! int peer; // which accceptor
Req(int user, int peer, String msg) {
this.msg = msg;
this.user= user;
this.peer = peer;
}
}
}
--- 375,452 ----
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 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 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 >